criteria api能从源头杜绝sql注入,因其查询构建不涉及字符串拼接,字段名来自实体属性,条件值作为参数传入,最终由hibernate用preparedstatement执行,用户输入仅作绑定参数而不参与sql解析。

Criteria API为什么能从源头杜绝SQL注入
因为整个查询构建过程不涉及任何字符串拼接——字段名来自实体类属性(root.get("username")),条件值通过方法参数传入(cb.equal(..., username)),最终生成的SQL由Hibernate在底层用PreparedStatement执行。用户输入永远只作为绑定参数,不会进入SQL语法解析阶段。
必须避免的三个“伪安全”写法
看似用了Criteria,但实际仍可能引入风险或导致功能异常:
-
cb.literal(userInput):把用户输入当字面量硬塞进SQL,绕过参数绑定 → ❌ 注入风险 -
cb.concat(root.get("name"), userInput):拼接操作发生在SQL表达式层,输入内容参与语义构造 → ❌ 危险 - 手动拼
"SELECT * FROM " + schemaName + " WHERE ..."再套Criteria:Criteria不处理FROM子句,这步已在框架外 → ❌ 完全失效
IN子句、空值、动态排序的正确处理方式
这些常见需求容易误入字符串拼接陷阱,必须严格按规范走:
- IN查询必须用
cb.in(root.get("status"))+inClause.value(statusList),不能用cb.equal(root.get("id"), idList) - 空集合需提前判断:
if (!ids.isEmpty()) { predicate = cb.and(predicate, root.get("id").in(ids)); } - 排序字段和方向不可参数化:
criteriaQuery.orderBy(cb.asc(root.get(sortField)))中的sortField必须来自白名单枚举或Set.of("name", "createdTime").contains(sortField)校验
字段名拼错、类型不匹配时的真实表现
很多人以为“写错了就查不到”,其实这是误导:
-
root.get("user_name")而实体中是userName→ 编译期无报错,运行时报IllegalArgumentException: Unable to locate Attribute -
cb.equal(root.get("age"), "25")传String给Integer字段 → 查询结果为空,但不会报错,也**不会**触发注入(类型不匹配导致条件恒假) - 用
cb.like(root.get("name"), "%" + userInput + "%")→ ❌ 看似没拼SQL,实则userInput已进入表达式树,若含%或_会改变语义,应改用cb.like(root.get("name"), cb.concat("%", cb.concat(userInput, "%")))
真正难察觉的是那些“查得出来但逻辑不对”的情况——比如大小写敏感的字段名拼写偏差,或隐式类型转换掩盖了本该失败的条件。这类问题不会报错,也不会被扫描工具发现,只能靠单元测试覆盖关键路径。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











