JPA动态SQL注入风险如何修复与安全防范
时间:2026-08-15 | 作者:穿越地图的猫 | 阅读:0@Query + @Param 仅防参数值注入,无法防御动态表名、字段名、排序字段等SQL元数据注入,因其绕过PreparedStatement;必须通过白名单校验或Criteria API等机制保障安全。
为什么 @Query + @Param 不能防住所有注入
@Query 配合 @Param 确实能防住参数值注入,但前提是整个 SQL 字符串是静态的。
一旦出现 ${}、#{} 或运行时拼接表名/字段名,防线就会立刻失效。因为这些语法在 JPA 解析阶段就被展开为原始字符串,绕过了 PreparedStatement 的预编译机制。
常见错误场景包括:
@Query("SELECT * FROM user WHERE name LIKE '%${name}%'")@Query(value = "SELECT * FROM #{#entityName}", nativeQuery = true)@Query("SELECT * FROM user ORDER BY #{#sortField}")
这些写法会让恶意输入直接进入 SQL 结构层。数据库解析器会把它当指令执行,而不是值。
${}是 SpEL 字符串插值,等同于 Ja va 的+拼接#{}在 nativeQuery 中用于动态元数据(如表名),无法参数化- ORDER BY、GROUP BY、LIMIT 后的字段或数字,都不能用
:param占位
动态表名/字段名必须走白名单校验
表名、列名、排序字段这些属于 SQL 元数据,JDBC 和 JPA 天然不支持参数化。
你不能写 SELECT :column FROM :table,运行时会报错。唯一安全的做法是:把允许的值固化为枚举或配置项,从用户输入映射到预设值,而不是直接代入。
例如排序字段只允许 "id"、"created_time"、"status"。
这时就用 switch 或 Map.of() 映射,遇到非法值直接抛 IllegalArgumentException。
- 禁止把
request.getParameter("sort")直接传进@Query字符串 - 白名单必须硬编码或加载自受控配置(如
@ConfigurationProperties),不能来自数据库或外部 API - SonarQube 报告的“SQL 注入”警告,在白名单场景下是误报,应配合
@SuppressWarnings("squid:CallToDeprecatedMethod")或注释说明校验逻辑
复杂动态查询优先用 Criteria API 或 Specification
一旦查询条件组合变得很灵活,比如一个搜索页就有 5 个可选字段,同时又要守住类型安全,继续手写 @Query 往往很快就会难以维护。
相比之下,Criteria API 和 Spring Data JPA 的 Specification 不需要靠拼接字符串来组织查询,而是基于 Ja va 对象映射生成 SQL。
字段名、操作符都直接取自实体类定义,因此天生就具备注入防护能力。
示例中 root.get("username") 的 "username" 是编译期确定的字符串字面量,不是运行时变量。
criteriaBuilder.like(...) 的值始终走 PreparedStatement 绑定。
- 简单单条件查询,
@Query+@Param足够,别过度设计 - 涉及 or/and 嵌套、权限过滤、多租户字段追加时,
Specification的组合能力(.and()/.or())比 if-else 拼 SQL 更可靠 - 注意
Specification不能替代白名单——它只管值和字段名,不管表名或排序方向(ASC/DESC)
LIKE 查询和模糊匹配的写法陷阱
LIKE 的通配符(%)必须由代码拼,不能交给用户输入。
错误写法是:@Query("SELECT u FROM User u WHERE u.name LIKE :pattern") + @Param("pattern") "%"+input+"%"。
这看似安全,但若 input 包含 _ 或 %,就会改变语义。更糟的是,有人写成 "%${input}%",直接破防。
更稳妥的方式是,把通配匹配这件事放在 Ja va 层处理,再把干净的参数值传下去。
JPA 本身就支持 criteriaBuilder.like(root.get("name"), "%" + name + "%") 这种写法;如果用原生查询,也可以写成 CONCAT('%', :name, '%')。
但有一点必须卡死:% 的位置不能交给前端决定。
- 用户输入的
name应先做长度限制(如 ≤ 50 字符),防止慢查询 - 若需支持前缀/后缀/全匹配,用独立参数区分,如
matchMode = "prefix",再由服务端决定加几个% - 避免在 SQL 里用
ESCAPE处理特殊字符——增加复杂度且易出错,不如在 Ja va 层 clean 输入
结论
真正难的不是写对一行 @Query,而是识别哪些地方“看起来像参数,其实不是参数”。
表名、字段名、排序方向、分组维度、函数名 这些都要在代码里划清边界。
应通过白名单把它们卡死,而不是依赖框架的“自动防护”。
免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。
相关文章
更多-
- 迅捷路由器怎么调信号最强,设置时要注意什么?
- 时间:2026-08-27
-
- vivo浏览器怎么卸不掉?原因和解决方法在这里
- 时间:2026-08-27
-
- OPPO R11s黑屏了,怎么强制恢复出厂设置?
- 时间:2026-08-27
-
- 飞利浦显示器包装盒有生产日期和保修期吗?怎么看?
- 时间:2026-08-27
-
- 联想新平板开机必须联网吗?怎么做?
- 时间:2026-08-27
-
- 平板横竖屏切换设置与问题解决
- 时间:2026-08-27
-
- 移动电源容量怎么测?要准备哪些工具?
- 时间:2026-08-27
-
- 荣耀90 Pro防水吗?防水级别多少?怎么用才安全
- 时间:2026-08-27
精选合集
更多大家都在玩
大家都在看
更多-
- 以下哪种食材被称为“地下苹果” 蚂蚁庄园今日答案9.18
- 时间:2026-09-17
-
- 蚂蚁庄园今天答题答案2026年9月18日
- 时间:2026-09-17
-
- 蚂蚁庄园答题今日答案2026年9月18日
- 时间:2026-09-17
-
- 蚂蚁庄园小课堂2026年9月18日最新题目答案
- 时间:2026-09-17
-
- 小鸡答题今天的答案是什么2026年9月18日
- 时间:2026-09-17
-
- 蚂蚁庄园每日答题答案2026年9月18日
- 时间:2026-09-17
-
- 蔬菜洗完掉色,说明是被染色了,是真的吗 蚂蚁庄园今日答案9月18日
- 时间:2026-09-17
-
- 满襟蜡绘花纹巧染就花纹当绣裳说的是哪种传统技艺 蚂蚁新村今日答案2026.9.17
- 时间:2026-09-17
