HQL拼接导致SQL注入是因为用户输入被直接嵌入HQL字符串,Hibernate将其视为完整查询而非参数,无法进行安全绑定;应始终使用setParameter等参数化方式。

为什么 HQL 拼接会导致 SQL 注入
HQL 本身不直接执行,而是由 Hibernate 解析后转成 SQL 再交给数据库。但如果你在写 HQL 时用 + 拼接用户输入,比如 "from User u where u.name = '" + username + "'",那 Hibernate 根本没机会做参数隔离——它拿到的已经是“拼完的字符串”,直接当完整 HQL 解析。这时候攻击者传入 m' or '1'='1,最终生成的 HQL 就是 from User u where u.name = 'm' or '1'='1',Hibernate 认为语法合法,照常翻译成 SQL 执行。
关键点在于:注入发生在 Hibernate 解析 HQL 阶段之前,不是数据库执行 SQL 阶段。所以“用了 Hibernate”不等于“自动防注入”。
- 拼接 HQL 的本质是把用户输入当作代码的一部分,而非数据
- Hibernate 对拼接后的字符串不做任何转义或类型检查,只负责解析语法
- 哪怕 HQL 不支持
union或系统表查询,只要能改变逻辑(如or 1=1、and 1=2),就可能绕过条件、查出不该查的数据
setParameter 和字符串拼接的根本区别
setParameter 是 Hibernate 提供的**参数绑定机制**,它把值和 HQL 模板分离处理。Hibernate 在内部会把命名参数占位符(如 :username)先保留,等真正生成 SQL 时,再把参数值以**预编译参数形式**交给 JDBC 驱动(例如 PreparedStatement.setString(1, value))。此时 value 被当作纯数据,单引号、分号、or 全部失去语法意义。
而字符串拼接是把值直接塞进 HQL 字符串里,Hibernate 看到的就是一个完整的、已“固化”的查询语句,没有“参数”概念可言。
-
createQuery("from User u where u.name = :name").setParameter("name", input)→ 安全 -
createQuery("from User u where u.name = '" + input + "'")→ 危险 - Hibernate 5+ 已废弃
?位置参数(setString(0, input)),仅推荐命名参数:xxx,更清晰且不易错位
HQL 注入的典型错误现象
你可能没看到数据库报错,但业务逻辑已经失控。常见表现包括:
- 搜索框输
admin' --后返回所有用户(注释掉后续条件) - 模糊查询传
%' or '1'='1,结果集远超预期 - 分页接口中
order by动态字段被拼接,导致传入id; DROP TABLE user报错或执行异常 SQL - 日志里出现类似
QuerySyntaxException: unexpected token: or—— 这是 Hibernate 解析失败,说明攻击已进入 HQL 层,但未必成功;若无报错反而更危险
注意:QuerySyntaxException 来自 Hibernate 解析器,PSQLException 或 MySQLSyntaxErrorException 才是数据库层报错。前者说明 HQL 被篡改但未通过校验,后者说明已穿透到 DB —— 两种都算漏洞暴露。
容易被忽略的“伪安全”写法
有些开发者以为做了过滤就万事大吉,其实并不可靠:
- 用
StringEscapeUtils.escapeSql()处理输入后再拼接 —— 这是面向旧版 JDBC 的思路,在 HQL 场景下多余且易漏(比如没处理 Unicode 编码绕过) - 对输入做
replaceAll("'", "''")—— HQL 不吃 SQL Server 风格转义,Hibernate 解析时仍会报错或行为异常 - 只对 Web 层参数做校验(如正则限制字母数字),但 Service 层调用 DAO 时又从其他来源拼接 HQL —— 漏洞出现在调用链深处
- 误以为 HQL 不支持
union就绝对安全 —— 实际上or、and、like、in都足以破坏业务逻辑
真正的边界只有一个:HQL 字符串是否包含任何运行时拼接的用户输入。只要存在,就必须重构为 setParameter 绑定。










