order by 后必须用 ${},因字段名属 sql 结构部分,无法预编译;#{} 会加引号导致语法错误,故只能通过白名单枚举校验后拼接 ${sortcolumn} 和 ${sortorder}。

ORDER BY 后必须用 ${},而不能用 #{} —— 这是硬限制,不是写法问题,是 SQL 语法决定的。绕不开,只能拦在前面。
为什么 QueryWrapper.orderByAsc(String column) 不能直接接前端参数
MyBatis-Plus 的 orderByAsc、orderByDesc 底层仍是字符串拼接字段名,不走预编译。它只对值(如 WHERE name = #{value})做 # 绑定,但字段名、排序方向、表名这些“SQL 结构部分”全靠拼接。
- 前端传
sortField=update_time; DROP TABLE user--,MP 的replaceAllBlank和SqlInjectionUtils拦不住注释、换行、空格变体(比如upda%09te_time) -
OrderItem.setColumn()只删空白字符,不校验语义合法性 - 哪怕字段名合法,
sortOrder若没限制为ASC/DESC,攻击者可传ASC, (SELECT @@version)实现注入
白名单枚举怎么写才真正防住注入
白名单不能只是个字符串列表,必须绑定三件事:前端 key、数据库真实字段、默认排序方向,并提供非法输入兜底逻辑。
public enum SortColumnEnum {
ID("id", "id", "DESC", true),
CREATE_TIME("createTime", "create_time", "DESC", true),
USER_NAME("userName", "user_name", "ASC", true);
private final String sortKey;
private final String dbColumn;
private final String defaultOrder;
private final boolean hasIndex;
SortColumnEnum(String sortKey, String dbColumn, String defaultOrder, boolean hasIndex) {
this.sortKey = sortKey;
this.dbColumn = dbColumn;
this.defaultOrder = defaultOrder;
this.hasIndex = hasIndex;
}
public static SortColumnEnum match(String sortKey) {
if (sortKey == null || sortKey.isBlank()) return ID;
for (SortColumnEnum e : values()) {
if (e.sortKey.equals(sortKey)) return e;
}
return ID; // 非法 key 兜底主键排序,不抛异常
}
public static String safeOrderDir(String dir) {
if ("asc".equalsIgnoreCase(dir)) return "ASC";
if ("desc".equalsIgnoreCase(dir)) return "DESC";
return null; // 返回 null,后续 fallback 到枚举默认方向
}
}
- 枚举构造时就固化字段映射,避免运行时反射或配置文件绕过
-
match()方法返回SortColumnEnum实例,而非原始字符串,后续所有拼接都基于e.dbColumn -
safeOrderDir()严格限定方向,返回null表示需 fallback,不拼接非法字符串
XML 中怎么安全使用 ${} 动态排序
XML 里只允许出现两个变量:${sortColumn} 和 ${sortOrder},且这两个值必须来自白名单校验后的结果,不能是原始请求参数。
- Controller 层校验后,把
SortColumnEnum.dbColumn和safeOrderDir(dir)结果塞进Map或 DTO - XML 中写成:
ORDER BY ${sortColumn} ${sortOrder},不加任何额外逻辑(比如<if></if>嵌套判断) - 严禁在 XML 中二次拼接、拼字符串、调用工具类 —— 白名单校验必须在 Java 层完成,XML 只负责输出
- 如果
sortOrder为null,XML 里应有默认 fallback,例如:ORDER BY ${sortColumn} ${sortOrder != null ? sortOrder : 'DESC'}
最易被忽略的一点:白名单必须和数据库索引强绑定。比如 USER_NAME 枚举项标记 hasIndex = true,但 DB 里 user_name 字段实际没建索引,就会触发 filesort,查大表时直接拖垮性能。白名单不只是安全开关,也是索引治理的落地抓手。











