thinkphp多语言漏洞本质是lang参数未经路径规范化校验直接拼接文件路径,6.0.13及以下版本存在目录遍历风险,可结合pearcmd.php与register_argc_argv实现rce;确认是否启用需检查middleware.php中loadlangpack注册、config/lang.php的switch配置及lang::setlangset调用;防御须前置realpath+白名单校验、关闭功能、升级至6.0.14+并禁用危险php配置。

ThinkPHP多语言功能本身不危险,危险的是 lang 参数被直接用于文件路径拼接且未做路径规范化校验——6.0.13 及以下版本默认行为正是如此,攻击者可传 ../../../../etc/passwd 或 ../../../../usr/local/lib/php/pearcmd 触发目录遍历+远程代码执行。
如何确认项目是否启用多语言中间件
ThinkPHP 6.x 的多语言加载由 LoadLangPack 中间件驱动,只要它在中间件队列中且未显式禁用,lang 参数就会被解析并尝试加载语言包。
- 检查
app/middleware.php是否包含\think\middleware\LoadLangPack::class(或类似注册) - 搜索项目中是否有调用
app()->lang->setLangSet()或Lang::setLangSet() - 查看
config/lang.php中'switch' => true—— 若为true,说明多语言功能逻辑已启用 - 即使没手动配置,某些模板引擎插件或第三方扩展也可能悄悄引入该中间件
lang 参数必须经过 realpath + 白名单根目录校验
框架原生的 input('lang') 或 $request->param('lang') 返回原始字符串,不做任何路径净化。不能只用 basename() 或 str_replace(),它们无法防御 %2e%2e%2f、.//、空字节截断等绕过手法。
- 正确做法是:先拼出待加载的语言包完整路径,再用
realpath()解析真实路径 - 将结果与预设的合法语言包根目录(如
realpath(config('lang.path')))比对前缀 - 若
realpath()返回false,或结果不以白名单目录开头,立即中止加载 - 示例关键校验逻辑:
$lang = $request->param('lang', 'zh-cn');
$baseDir = realpath(config('lang.path', app()->getAppPath() . 'lang'));
$target = realpath($baseDir . DIRECTORY_SEPARATOR . basename($lang));
if ($target === false || strpos($target, $baseDir) !== 0) {
throw new HttpException(400, 'Invalid lang parameter');
}
关闭多语言功能是最简单有效的防御手段
如果你的项目实际并不需要多语言支持(比如仅面向中文用户),关闭它比加固更可靠。很多团队升级了框架却仍因残留配置暴露风险。
- 在
config/lang.php中强制设置:'switch' => false - 移除
app/middleware.php中的LoadLangPack类引用 - 检查所有控制器、中间件、事件监听器,删除对
Langfacade 的调用 - 上线前用 curl 测试:
curl "https://yoursite.com/?lang=../etc/passwd",确认返回 400/404 而非文件内容
升级 ThinkPHP 并禁用危险 PHP 配置
官方已在 6.0.14+ 和 6.1.0+ 版本修复该漏洞,但仅升级不够——PEAR 组件和 PHP 配置仍是攻击链关键一环。
- 执行
php -r "echo \think\facade\App::version();"确认当前版本 ≥ 6.0.14 - 检查
php.ini中是否开启register_argc_argv = On(Docker 默认开),应设为Off - 确认系统未安装 PEAR 或已删除
/usr/local/lib/php/pearcmd.php(非必需组件) - 生产环境务必设置
disable_functions = exec,passthru,shell_exec,system,proc_open,popen
最常被忽略的一点:即使你写了路径校验逻辑,若把它放在中间件之后的业务层(比如某个控制器里),攻击者仍可能在中间件加载语言包时就完成利用——校验必须前置到 LoadLangPack 内部或通过自定义中间件提前拦截 lang 参数。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











