ThinkPHP 5.1 统计查询字段名必须白名单校验,因 count()/sum() 等方法首参为字段名,无法参数化绑定,直传用户输入将导致聚合函数注入(如 CVE-2024-36837);须禁用动态拼接、升级至 5.1.26+、关闭调试并加 WAF 防御。

ThinkPHP 5.1 的统计查询(如 count()、sum()、max()、min()、avg())本身不直接执行参数化绑定,因为它们的第一个参数是**字段名**,而 SQL 中的字段名、表名无法用问号或命名占位符参数化。一旦该字段名来自用户输入且未校验,就会触发注入——这正是 ThinkPHP 5.1.24–5.1.25 被爆聚合函数注入漏洞(CVE-2024-36837 类)的根本原因。
必须白名单校验字段名
所有统计方法的首参(字段名)严禁直传 input()、param() 或 $_GET 等用户输入。正确做法是限定为预定义的合法字段列表:
- 定义安全字段白名单:
$allowedFields = ['id', 'price', 'score', 'create_time', 'status']; - 校验并兜底:
$field = input('field', 'id'); $realField = in_array($field, $allowedFields) ? $field : 'id'; - 再调用统计:
db('user')->count($realField);或db('order')->sum($realField);
禁用动态字段拼接,拒绝字符串构造
以下写法全部高危,必须清除:
-
db('user')->count(input('col'))—— 字段名直传 -
db('user')->max($_GET['field'] . '`)#')—— 利用反引号逃逸 -
db('user')->sum($request->param('f') . ' FROM users WHERE 1=1 #')—— 拼接恶意后缀
特别注意:ThinkPHP 的 parseKey() 在聚合场景下($strict = true)会无条件包裹反引号,不做任何字符过滤,所以正则或 addslashes() 都无效,唯一解法是源头白名单。
立即学习“PHP免费学习笔记(深入)”;
避免使用 exp/inc/dec 等表达式模式
统计本身虽不支持 exp,但若在关联查询或子查询中混用(例如 where('amount', 'exp', 'SUM(' . input('col') . ')')),同样危险。规则统一:
- 禁止用户控制任何出现在 SQL 字段/函数名位置的内容
- 如需动态聚合逻辑,改用条件分支硬编码:
if ($col === 'price') { $val = db()->sum('price'); } - 彻底移除控制器中类似
->count(input('field'))的调用,全局搜索->count(、->sum(、->max(并逐个审计
升级与辅助加固措施
技术债要清,防护要闭环:
- 升级至 ThinkPHP 5.1.26+,该版本已修复
parseKey在聚合调用中的绕过缺陷 - 关闭调试模式:
'app_debug' => false,防止报错泄露数据库结构 - 对所有统计入口启用日志记录,监控异常字段名(如含括号、反引号、#、-- 的请求)
- 在 Nginx/Apache 层加简单 WAF 规则拦截 URL 中出现
%60(反引号 URL 编码)、%23(#)等可疑字符
不复杂但容易忽略。



















