group by和having无法参数化,必须用白名单校验字段名、强转数值型输入,否则直接拼接用户输入将导致sql注入;数据库协议层限制使其不支持?占位符绑定结构。

GROUP BY 和 HAVING 本身不能参数化,直接拼接用户输入等于敞开注入大门。 数据库不支持对分组字段、排序方向、HAVING 条件中的列名或函数名做预编译绑定——这是 SQL 协议层限制,不是框架缺陷。你传什么,它就解析什么。
为什么 GROUP BY 字段不能像 WHERE 那样用 ? 占位符?
因为 SQL 标准规定:GROUP BY 后必须是确定的列名、表达式或位置序号,不能是运行时才知值的参数。PreparedStatement 只能绑定“值”,不能绑定“结构”。GROUP BY ? 在所有主流数据库(MySQL、PostgreSQL、SQL Server)中都会报语法错误,比如 MySQL 的 ERROR 1064。
- 试图用
ORDER BY ?或GROUP BY ${sortField}动态传字段名,就是把校验权交给攻击者 -
GROUP BY 1看似绕过字段名检查,但 SELECT 列序一调,逻辑全错;SQL Server 直接不认 - 哪怕只拼一个数字(如
HAVING COUNT(*) > ${threshold}),也得先intval()或(int)强转,否则可能被注入1; DROP TABLE users--
MyBatis 里动态 GROUP BY 怎么写才安全?
必须严格区分 #{} 和 ${} 的使用边界:#{} 只能用于值,${} 才能拼字段名——但 ${} 没有自动转义,必须前置白名单校验。
- 定义允许的分组字段白名单:
$allowed_groups = ['user_id', 'status', 'created_at']; - 从请求取值后立刻校验:
in_array($input_group, $allowed_groups, true),不通过就拒掉 - XML 中用
${groupField}拼接,但前提是它已通过白名单 —— 不要试图对${}做 escape 或 quote - 禁止任何形式的字符串拼接:
"GROUP BY " . $input或concat("GROUP BY ", $input)全部高危
HAVING 条件里混入用户输入有多危险?
HAVING 表面像 WHERE,实则更难防:它常含聚合函数,攻击者可利用语法错误触发报错注入(如 HAVING 1=1 配合 GROUP BY 爆字段),尤其在显错模式下。
-
HAVING COUNT(*) > ${n}:必须强制转为整型,(int)$n比filter_var($n, FILTER_SANITIZE_NUMBER_INT)更可靠 -
HAVING status = '${status}':绝对禁止——status 是字段值,但出现在 HAVING 里说明它本该在 GROUP BY 中,此时应走白名单校验字段值,而非拼字符串 - 若业务真需动态条件(如“订单数大于 X”),X 必须是服务端可控参数,不能来自前端 raw input
- 别信“只传数字就安全”:浮点数、科学计数法(
1e9)、负数都可能绕过弱校验
最易被忽略的一点:GROUP BY 和 HAVING 的安全性不取决于你用了多“高级”的 ORM,而取决于你是否把字段名、函数名、聚合阈值这些非值类输入,当成不可信输入来处理。白名单校验不是可选项,是必选项;类型强转不是防御补充,是第一道闸门。











