白名单校验字段名、禁用exp模式、数组参数强类型检查可消除90% ThinkPHP SQL注入风险;order/group by动态排序需白名单过滤并用parseKey处理字段名;where中禁用exp模式,改用参数绑定或闭包构造。

直接用白名单校验字段名、禁用 exp 模式、对数组参数做类型强检——这三步做完,90% 的 ThinkPHP SQL 注入风险就没了。
order/group by 动态排序时传参被拼进 SQL
框架对 order() 和 group() 的参数不做自动过滤,尤其当传入数组时,键名(key)会被原样拼进 SQL。比如 $order = input('orderby'); + db('user')->order($order)->select(),攻击者传 ?orderby[id]=updatexml(1,concat(0x7e,user()),0) 就能触发报错注入。
- 只允许白名单内的字段参与排序:
in_array($order, ['id', 'name', 'create_time'], true),不匹配就 fallback 到默认值 - 手动拼接时必须用
parseKey()处理字段名(如db()->order($builder->parseKey($field) . ' ' . $sort)),避免符号绕过 - 禁止把用户输入直接丢进
order(),哪怕加了htmlspecialchars()也没用——它不防 SQL 关键字
where() 中滥用 exp 模式等于主动开后门
where('field', 'exp', $value) 是明确告诉框架“把 $value 当原生 SQL 执行”,等同于手写拼接。传 ?sql=1 and updatexml(1,concat(0x7e,database()),0) 就能注入。
- 彻底删除所有
'exp'出现的地方,没有例外 - 改用参数绑定:比如
where('id', '>', input('min_id', 0, 'intval'))或where(['status' => 1, 'type' => ['in', [1,2,3]]]) - 如果真需要动态表达式(极少见),用闭包构造:
where(function ($query) { $query->where('a', '>', 1)->whereOr('b', ',让框架自己处理绑定
update/insert 接收数组参数时触发 parseData 漏洞
ThinkPHP 5.1.0–5.1.7 中,update(['field' => $input]) 若 $input 是用户可控数组(如 name="username[a]"),会进入 parseData() 分支,对数组结构解析——若含 ['inc', 'updatexml()', 1] 这类结构,直接拼入 SQL。
立即学习“PHP免费学习笔记(深入)”;
- 所有接收的数组型参数必须做类型强校验:
is_string($val) || is_numeric($val),拒绝多维或含函数名的数组 - 用
input('data/a', '', 'htmlspecialchars')等过滤器时,注意它只处理字符串,对数组无效——得先is_array()判断再遍历校验 - 升级到 5.1.38+ 可缓解,但不能替代代码层校验;老版本建议打补丁或重写数据接收逻辑
最隐蔽的坑是:PDO 预处理开启后仍可能被绕过——不是框架问题,而是数据库报错注入(如 updatexml)在 prepare 阶段就能回显数据。别依赖“用了预处理就安全”,关键还是别让任何用户输入靠近 SQL 字符串拼接点。



















