如何修复JPA动态SQL中的注入风险

陌辰小哥_8585

陌辰小哥_8585

2026-08-14

571人浏览

原创

@query + @param 仅防参数值注入,无法防御动态表名、字段名、排序字段等sql元数据注入,因其绕过preparedstatement;必须通过白名单校验或criteria api等机制保障安全。

如何修复jpa动态sql中的注入风险

为什么 @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 字符串插值,等同于 Java 的 + 拼接
  • #{} 在 nativeQuery 中用于动态元数据(如表名),无法参数化
  • ORDER BY、GROUP BY、LIMIT 后的字段或数字,都不能用 :param 占位

动态表名/字段名必须走白名单校验

表名、列名、排序字段这些属于 SQL 元数据,JDBC 和 JPA 天然不支持参数化。你不能写 SELECT :column FROM :table,运行时会报错。唯一安全的做法是:把允许的值固化为枚举或配置项,从用户输入映射到预设值,而不是直接代入。

例如排序字段只允许 "id""created_time""status",那就用 switchMap.of() 映射,遇到非法值直接抛 IllegalArgumentException

  • 禁止把 request.getParameter("sort") 直接传进 @Query 字符串
  • 白名单必须硬编码或加载自受控配置(如 @ConfigurationProperties),不能来自数据库或外部 API
  • SonarQube 报告的“SQL 注入”警告,在白名单场景下是误报,应配合 @SuppressWarnings("squid:CallToDeprecatedMethod") 或注释说明校验逻辑

复杂动态查询优先用 Criteria API 或 Specification

当条件组合多变(比如搜索页有 5 个可选字段)、且需保持类型安全时,手写 @Query 很快会失控。Criteria API 和 Spring Data JPA 的 Specification 不拼字符串,靠 Java 对象反射生成 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}%",直接破防。

正确做法是:在 Java 层处理通配逻辑,再传纯参数值。JPA 支持 criteriaBuilder.like(root.get("name"), "%" + name + "%"),也支持原生查询中用 CONCAT('%', :name, '%'),但绝不能让前端控制 % 位置。

  • 用户输入的 name 应先做长度限制(如 ≤ 50 字符),防止慢查询
  • 若需支持前缀/后缀/全匹配,用独立参数区分,如 matchMode = "prefix",再由服务端决定加几个 %
  • 避免在 SQL 里用 ESCAPE 处理特殊字符——增加复杂度且易出错,不如在 Java 层 clean 输入

真正难的不是写对一行 @Query,而是识别哪些地方“看起来像参数,其实不是参数”。表名、字段名、排序方向、分组维度、函数名……这些都得在代码里划清边界,用白名单卡死,而不是依赖框架的“自动防护”。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

3743

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

791

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

969

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

5521

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

2503

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

5500

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

7181

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

970

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

852

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.1万人学习