动态拼接hql字符串是最大风险源,必须禁用+或stringbuilder拼接用户输入,对字段名、排序方向等sql结构要素实施白名单校验,in子句须用setparameterlist(),order/group by字段不可参数化,hibernate 6中需显式指定参数类型。

动态拼接 HQL 字符串是最大风险源
只要你在代码里用 + 或 StringBuilder 把用户输入直接拼进 HQL,就等于把数据库钥匙交到攻击者手上。比如 "from User u where u.name = '" + username + "'" 这种写法,哪怕只漏掉一个单引号转义,username 输入 ' OR 1=1 -- 就能绕过全部校验。
真正危险的不是“会不会被注入”,而是“哪段拼接逻辑被忽略了”。常见高危场景包括:分页动态排序字段、多条件组合查询、IN 子句构造、子查询嵌套、视图或原生 SQL 的 WHERE 片段。
- 不要对字段名、表名、排序方向(
ASC/DESC)做参数绑定——它们不是值,而是 SQL 结构,必须走白名单校验 - 避免用
StringEscapeUtils.escapeSql()临时补救:它只处理单引号,对布尔型注入(1=1)、注释符(--)、函数调用(CONCAT())无效 - 原生 SQL(
createSQLQuery())比 HQL 更危险,因为 Hibernate 不会帮你解析结构,所有拼接都裸奔
IN 子句必须用 setParameterList(),不能 setParameter()
setParameter("ids", Arrays.asList(1,2,3)) 看似合理,但 Hibernate 会把它当做一个 Object 参数传入,最终生成类似 WHERE id = ? 的单值语句,而不是 WHERE id IN (?, ?, ?)。结果要么报错,要么全表扫描,甚至被绕过。
正确做法是显式调用 setParameterList(),让 Hibernate 自动展开占位符:
Query query = session.createQuery("from User u where u.id in :ids");
query.setParameterList("ids", Arrays.asList(1L, 2L, 3L));
- 仅适用于命名参数(
:ids),位置参数(?0)不支持setParameterList() - 列表为空时,Hibernate 6 默认抛出异常;如需兼容空列表,得提前判断并改写 HQL(例如加
or 1=0) - 注意类型一致性:
Long列表不能传给Integer字段,否则可能触发隐式转换失败
ORDER BY 和 GROUP BY 字段不能参数化,必须白名单控制
HQL 不允许把排序字段名作为参数绑定,order by :sortField 会直接抛出 ParsingException。试图用 setParameter("sortField", "userName") 是徒劳的。
可行方案只有两个:硬编码可选字段,或用 Criteria API 动态构建:
- 白名单示例:
if (!Arrays.asList("userName", "createTime", "status").contains(sortField)) throw new IllegalArgumentException(); - 拼接时严格限制格式:
"order by " + sortField + " " + (ascending ? "asc" : "desc"),其中ascending是布尔开关,非用户输入 - Criteria API 虽重但安全:
criteria.orderBy(cb.asc(root.get(sortField))),前提是sortField已从白名单映射为Path
Hibernate 6 的 setParameter() 默认类型推断有陷阱
在 Hibernate 6 中,setParameter("name", value) 会根据 value 类型自动选择 JDBC 类型(如 String → VARCHAR)。但若 value 是 null 或泛型擦除后的 Object,可能误判为 OTHER,导致数据库驱动无法正确绑定。
尤其在联合查询或自定义方言下,这种隐式推断容易引发类型不匹配错误,间接暴露底层结构,增加分析难度。
- 建议显式指定类型:
setParameter("name", name, StandardBasicTypes.STRING) - 对枚举、LocalDateTime、JSON 字段,务必用对应方言类型(如
PostgreSQLEnumType),否则可能跳过转义逻辑 - 慎用
setProperties():它依赖反射获取字段值,若实体含计算属性或 getter 有副作用,可能引入不可控行为











