必须严格参数绑定且禁止拼接用户可控结构,表名字段名排序方向需白名单校验,in子句须用setparameterlist,空列表需单独判断,优先使用criteria api防注入。

必须用参数绑定,且不能拼接任何用户可控的结构部分——表名、字段名、排序方向、IN列表本身都不能靠字符串拼出来。
命名参数写对了但没调用 setParameter() 就等于没防注入
HQL 中写 :username 只是声明占位符,createQuery() 阶段完全不校验参数是否绑定。漏掉 query.setParameter("username", input),运行时直接抛 QueryParameterException,查不到数据,功能中断,且无编译提示。
常见错误:
-
:userName和setParameter("username", ...)大小写不一致 → 报错 - HQL 写
:user_name却传"username"→ 同样报错 - 只创建
Query对象,忘了后续绑定动作 → 查询逻辑静默失效
IN 子句必须用 setParameterList(),不能塞进 setParameter()
把 List<long></long> 直接传给 setParameter("ids", idList) 是典型错误。Hibernate 会把这个 List 当作单个对象处理,生成类似 WHERE id = ?(? 对应整个 List),导致语法错误或全表扫描。
正确做法只有一种:
- 用
setParameterList("ids", idList),Hibernate 才会自动展开成WHERE id IN (?, ?, ?) - 手动拼
"IN (" + String.join(",", ids) + ")"→ 注入风险 + 类型转换问题 - 空列表必须单独判断:
setParameterList()不接受空集合,会抛IllegalArgumentException
表名、字段名、排序方向无法参数化,必须白名单硬校验
HQL 和原生 SQL 都不支持把 FROM 后的表名、ORDER BY 后的字段、ASC/DESC 这类结构当参数绑定。任何字符串拼接都是非法且危险的:
-
createQuery("ORDER BY " + sortField + " " + sortOrder)→ ❌ 排序方向若来自用户输入,可能执行ASC; DROP TABLE user; - 白名单示例:
if (!Set.of("user", "order", "product").contains(tableName)) throw new IllegalArgumentException(); - 排序方向只允许字面量
"ASC"或"DESC",禁止动态传入
优先用 Criteria API,而不是手写 HQL
Criteria API 是唯一能从源头杜绝 HQL 注入的方式:整个查询由 Java 方法链构建,没有字符串参与拼接,字段名是 Java 属性名,拼错直接编译失败。
适用边界很明确:
- 简单条件、关联查询、类型安全场景 →
CriteriaBuilder是首选 - 需要
GROUP_CONCAT、复杂子查询嵌套、数据库特有函数时 → 回退到 HQL,但必须严格参数绑定 - 原生 SQL 查询(
createSQLQuery())只能用?占位符 + 位置绑定,不支持命名参数
最易被忽略的一点:白名单校验和空集合判断不是“可选优化”,而是强制前置步骤;一旦跳过,参数绑定再规范也挡不住结构层注入。











