order by字段不能参数化是因为数据库语法不支持占位符作为列名,会将其视为字符串字面量导致排序失效;必须通过白名单校验列名和方向后拼接sql。

ORDER BY字段为什么不能用参数化?
因为数据库语法不支持——ORDER BY ?在MySQL、PostgreSQL、SQL Server里都不会被解析为列名,而是当成字符串字面量排序,结果既不报错也不生效,容易误判“已防护”。SELECT * FROM user ORDER BY ?实际等价于按常量?排序,所有行顺序不变。真正起作用的列名必须作为SQL结构的一部分拼进去,所以必须靠白名单兜底。
MyBatis-Plus的orderByAsc(String)不是参数化
它内部是字符串拼接,不走#{} 绑定流程。传"id; DROP TABLE user--"或"name ASC, (SELECT 1)"会原样进SQL。安全写法只有一种:QueryWrapper.orderByAsc("created_at")(硬编码)。若需动态,必须先校验再拼:
- 用
SqlInjectionUtils.check(sortField)判断是否含select、union、;等关键词(MP内置) - 再用正则
^[a-zA-Z_][a-zA-Z0-9_]*$匹配合法标识符(注意去首尾空格) - 方向必须单独校验:
if (!"asc".equalsIgnoreCase(dir) && !"desc".equalsIgnoreCase(dir)) throw new IllegalArgumentException()
PHP/Go/Java通用白名单校验要点
白名单不是简单in_array或switch,要防绕过:
- PHP:必须用
in_array($col, ['id', 'name', 'updated_at'], true),第三个参数true防止"0" == false类型弱比较漏判 - Go:用
strings.TrimSpace()清理输入后再regexp.MatchString(`^[A-Za-z0-9_]+$`, col),否则" id "可能逃逸 - Java:定义
enum SortColumn { ID, NAME, CREATED_AT },用Enum.valueOf()转换,异常直接拒掉;方向同理用SortOrder.ASC/DESC - 别漏方向校验——只校列名放行
ASC, (SELECT password FROM user)仍可注入
PageHelper.orderBy()能替代${}吗?
能,但有前提:PageHelper.orderBy("create_time asc")内部做了字段白名单+方向校验,比裸${sort}安全。但它不自动过滤用户输入,你仍需自己做校验:
- 不能直接
PageHelper.orderBy(request.getParameter("sort")) - 必须先映射:
Map<string string> safeMap = Map.of("time", "create_time", "click", "view_count");</string> - 再取值:
String realCol = safeMap.getOrDefault(userInput, "create_time"); - 最后拼:
PageHelper.orderBy(realCol + " " + safeDir);
真正危险的从来不是“要不要排”,而是“排哪个字段”和“按什么方向排”——这两个都得落在白名单里,缺一不可。校验逻辑一旦漏掉空格、大小写、方向枚举,就等于没设防。











