HQL安全需严格绑定参数,漏绑致运行时异常;动态结构须白名单校验;Criteria API可从源头防注入。

只要没调用 setParameter() 或等效绑定方法,再规范的 HQL 也防不住注入。
命名参数写对了但没绑定,照样报错或逻辑失效
HQL 中写 :name 只是声明占位符,不是安全措施。Hibernate 不会在 createQuery() 阶段做任何参数校验或值替换——它只解析语法、生成 Query 对象。
- 漏掉
query.setParameter("name", value)→ 运行时抛QueryParameterException,查不到数据,不是注入,但功能直接断掉 - 参数名拼错(比如 HQL 写
:userName却传"username")→ 同样报QueryParameterException,大小写敏感 - 用了
setString("name", value)等旧方法 → Hibernate 5.2+ 已废弃,类型推导弱,某些场景(如枚举、自定义类型)可能出错
别用字符串拼接构造 HQL,哪怕只拼 WHERE 条件
动态字段、表名、排序方向(ASC/DESC)、IN 子句长度,都不能靠参数化解决。下面全是非法写法:
session.createQuery("FROM " + tableName + " WHERE id = :id") // ❌ tableName 无法参数化
session.createQuery("ORDER BY " + sortField + " " + sortOrder) // ❌
这类结构必须走白名单控制:
- 表名/字段名 → 映射到预设枚举或
Set.of("user", "order", "product")校验 - 排序方向 → 仅允许
"ASC"或"DESC"字符串字面量,禁止直接拼入 -
IN参数列表 → 用setParameterList("ids", idList),不要手动拼(?, ?, ?)
优先用 Criteria API 替代手写 HQL
Criteria 是唯一能从源头杜绝 HQL 注入的方式:整个查询结构由 Java 方法链构建,没有字符串参与拼接。
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
cq.select(root).where(cb.equal(root.get("username"), username));
List<User> users = session.createQuery(cq).getResultList();
注意点:
- 字段名(如
"username")是 Java 属性名,不是 SQL 列名,受编译器检查,拼错直接编译失败 - 值仍需传变量(如
username),但已脱离 SQL 文本上下文,不可能被解释为代码 - 不支持复杂函数(如
GROUP_CONCAT)、子查询嵌套过深时,还是得回退到 HQL + 严格参数绑定
原生 SQL 查询必须用 setParameterList() 处理 IN 列表
HQL 的 IN 支持 setParameterList(),但原生 SQL(createSQLQuery())同样适用,且更关键——因为原生 SQL 更容易滑向拼接陷阱。
- 错误写法:
"WHERE id IN (" + String.join(",", ids) + ")"→ 一旦ids含恶意字符串就中招 - 正确写法:
query.setParameterList("ids", idList),Hibernate 自动展开为合适数量的?占位符 - 注意:
setParameterList()仅适用于集合类型参数,不能用于单值;单值仍用setParameter()
真正危险的从来不是 HQL 语法本身,而是把用户输入当作代码片段去拼接。参数绑定不是加个冒号就完事,得确保每次查询都走到 setParameter() 这一步;动态结构没法参数化,就得靠白名单硬卡;而 Criteria API 是少有的、能把“写错就编译不过”这个安全边界前移到开发阶段的方案。

















