spring data jpa的派生方法、@query命名参数等规范化查询天然防sql注入,因hibernate自动生成preparedstatement并绑定参数;但@query(nativequery=true)、字符串拼接、动态表名列名需严格校验。

Spring Data JPA 的规范化查询(如派生方法、@Query 配合命名参数)默认就防 SQL 注入,前提是不碰字符串拼接、不硬塞动态表名/列名、不滥用原生 SQL 的结构拼接。
哪些查询方式天然免疫 SQL 注入
这些写法由 Hibernate 底层生成 PreparedStatement,参数全程绑定,无需额外处理:
-
findByUsername(String username)、findByStatusAndRole(String status, String role)等派生方法:参数自动走setString()或setLong(),安全 -
@Query("SELECT u FROM User u WHERE u.email = :email")+@Param("email"):命名参数被 Hibernate 解析为预编译占位符,不是字符串替换 -
@Query("SELECT u FROM User u WHERE u.id IN :ids")+@Param("ids") List<long></long>:Hibernate 会把集合展开成对应数量的?占位符 - 所有
JpaRepository自带方法(save()、findById()、findAll(Specification)):不暴露 SQL 构造过程,底层调用标准 JPA API
@Query(nativeQuery = true) 是高危区,但不是不能用
原生 SQL 绕过 Hibernate 解析层,直接交由 JDBC 执行。一旦你用 + 拼接变量,防线立刻失效:
- 错误示范:
@Query(value = "SELECT * FROM user WHERE name = '" + name + "'", nativeQuery = true)—— 直接触发注入 - 正确做法:只用
?1、?2或:name占位,且仅通过@Param传值 - 注意:
IN动态列表在原生 SQL 中不支持直接绑定:ids,会报错;必须提前确定长度,例如IN (?2, ?3, ?4),再传三个参数 - 若真需动态字段(如排序字段),不能写
ORDER BY :sortField,JPA 不识别;应白名单校验后硬编码,或改用JdbcTemplate
动态条件别拼 SQL,用 Specification 替代
业务常要“按需加 WHERE”,有人用 StringBuilder 拼 JPQL 再塞进 @Query,这是最典型的破防操作——@Query 是编译期固定的,运行时无法改变结构。
-
JpaSpecificationExecutor+Specification是正解:条件用 Java 类型安全构建,最终仍生成预编译语句 - 示例:
cb.like(root.get("name"), "%" + name + "%")中的name是纯参数值,%写死在代码里,不来自用户输入 - 嵌套条件、多表关联、复杂
OR/AND组合,都可通过Specifications.where(...).and(...).or(...)安全组合 - 避免在 service 层偷偷用
String.format拼完 SQL 再丢给JdbcTemplate——这种代码不报错,但等于裸奔
命名参数能绑值,但不能绑结构
这是最容易踩的坑:以为写了 @Query("SELECT * FROM :table WHERE name = :name") 就算用了参数化,其实 :table 不会被替换,而是当字面量处理,轻则查不到数据,重则报语法错。
-
:param只能在值上下文中使用:WHERE、HAVING、ORDER BY(仅值部分,如ORDER BY name :direction中的:direction不安全)、GROUP BY(同理) - 表名、列名、函数名、排序方向(
ASC/DESC)必须硬编码,或经白名单校验后拼接 - 若必须动态列名,先做基础清洗(如
StringUtils.replaceChars(fieldName, ".", "_")),再比对Set.of("user_name", "created_at"),不匹配就抛IllegalArgumentException -
LIKE中的通配符必须写死在 SQL 里(如LIKE %:name%),不能由用户传入"%admin%"——否则可能绕过参数边界
真正难防的不是语法怎么写,而是团队里有人在某个角落用 String.format 或 StringBuilder 拼 SQL,还自以为“我用了 @Query”。防御的关键在于统一规范 + 代码审查 + 白名单兜底,而不是依赖某一行注解是否加了冒号。











