ThinkPHP 6.0.4 存在多表联合查询SQL注入漏洞,源于join等方法未校验用户输入的表名、字段名;需白名单限制、禁用字符串拼接、升级至6.0.15+、禁用raw()处理用户数据。

ThinkPHP 6.0.4 存在的多表联合查询(join)相关 SQL 注入漏洞,主要源于对用户传入的字段名、表名、别名等未做严格校验,当开发者直接将外部参数拼入 join()、field()、table() 等方法时,可能触发注入。该漏洞已在后续版本修复,但若暂无法升级,需从代码层加固。
严格校验 join 中的表名与字段名
避免将用户输入(如 URL 参数、POST 数据)直接用于 join() 的表名或 ON 条件中的字段名。应使用白名单机制限制可选值。
- 例如:用户请求按“部门”或“角色”关联,不要写
$query->join(input('join_table'), ...),而应映射为预设值:$allowTables = ['dept' => 'think_dept', 'role' => 'think_role'];<br>$table = $allowTables[input('join_table')] ?? 'think_user';<br>$query->join($table, 'user.dept_id=dept.id'); - ON 条件中涉及的字段名也需校验,禁止出现点号(
.)、反引号(`)、括号等特殊字符,可用正则/^[a-zA-Z_][a-zA-Z0-9_]*$/匹配合法标识符。
禁用动态 table/alias/field 字符串拼接
ThinkPHP 的 table()、alias()、field() 方法若接收用户输入的字符串且未过滤,极易引入注入。务必改用数组或闭包方式构建查询。
- ❌ 错误示例:
$query->table('think_user '.input('suffix')); - ✅ 正确做法:
使用数组指定表与别名:$query->table(['think_user' => 'u']);
字段用数组明确列出:$query->field(['u.id', 'u.name', 'd.title']);
复杂字段用闭包:$query->field(function ($f) { return $f->raw('COUNT(*) as total'); });
升级到安全版本并启用查询日志审计
官方已在 ThinkPHP 6.0.13+ 版本中增强对 join()、view() 等方法的参数过滤逻辑,强烈建议升级至 6.0.15 或更高稳定版。
立即学习“PHP免费学习笔记(深入)”;
- 升级后,检查配置
app.sql_explain设为true,开启 SQL 日志;结合中间件记录所有含JOIN的查询语句,人工抽检是否存在非常规表名或嵌套子查询。 - 若业务必须兼容旧版,可在全局中间件中拦截含
join、table、field的请求参数,对值进行htmlspecialchars()+ 正则校验双重处理,并拒绝非法字符请求。
避免使用 raw() 和 expression() 处理用户数据
raw() 和 expression() 会绕过 ThinkPHP 的参数绑定机制,一旦混入用户输入即高危。仅限硬编码 SQL 片段使用。
- ❌ 危险写法:
$query->where('status', 'exp', Db::raw(input('expr'))); - ✅ 替代方案:
所有动态条件改用参数绑定:$query->where('status', input('status', 1, 'intval'));
复杂表达式拆解为多个where()链式调用,或使用whereBetween()、whereIn()等安全方法。



















