Order By字段无法参数化,必须白名单兜底:列名和排序方向均需严格校验,否则易遭SQL注入;主流数据库不支持占位符,MyBatis-Plus的orderByAsc实为字符串拼接,${}更绕过所有过滤。

Order By字段无法参数化,必须白名单兜底
MySQL、PostgreSQL 等主流数据库不支持对 ORDER BY 后的列名或排序方向使用占位符(如 ? 或 $1),因为列名属于 SQL 语法结构,不是运行时数据。即使你写成 SELECT * FROM user ORDER BY ?,驱动只会把问号当字符串值处理,最终查出来的结果是按字面量 ? 排序——根本不会生效,更不会报错,容易误以为“安全”。
MyBatis-Plus 的 orderByAsc(String column) 不等于预编译
这个方法看着像参数化,实则内部仍是字符串拼接:column 被直接插入生成的 SQL 中,不走 #{} 绑定。所以传入 "id; DROP TABLE user--" 或 "name ASC, (SELECT 1 FROM dual)" 会原样拼进语句,触发注入。
-
QueryWrapper.orderByAsc("update_time")✅ 安全(硬编码) -
QueryWrapper.orderByAsc(request.getParameter("sort"))❌ 高危 -
QueryWrapper.orderByAsc("${sortField}")在 XML 中等同于裸拼,完全绕过 MP 的任何过滤
Go / PHP / Yii 中白名单校验的落地要点
白名单不是“简单 in_array”,得覆盖常见绕过手段:
- Go 用正则
^[A-Za-z0-9_]+$校验列名,但必须配合strings.TrimSpace去首尾空格,否则" id "可能逃逸 - PHP 中
in_array($sort, ['id', 'name', 'created_at'], true)必须加第三个参数true,避免类型弱比较("0" == false这类陷阱) - Yii 框架若用
->addOrderBy([$sort => $direction]),仍需提前校验$sort和$direction,因为该方法只对值做绑定,不对键名做防护
白名单漏掉排序方向也会被注入
只校验列名,放行任意 sortOrder 值,攻击者可构造 ASC, (SELECT password FROM user LIMIT 1) 这类 payload。必须同时限制方向为严格枚举:
- Go:只接受
asc/desc字符串,且转小写后比对 - Java:用
enum SortOrder { ASC, DESC },拒绝非枚举值 - PHP:
if (!in_array(strtolower($order), ['asc', 'desc'])) { die(); }
真正危险的从来不是“能不能排”,而是“排什么”和“怎么排”这两处都失控——白名单必须覆盖字段名 + 方向两个维度,缺一不可。

















