Spring Boot 项目如何防止 SQL 注入攻击?代码规范与最佳实践
Spring Boot 本身没有一键开启的“防 SQL 注入开关”,核心在于正确使用 ORM 框架的参数绑定功能,避免手动拼接 SQL 字符串。
先说结论:防御 SQL 注入主要靠代码规范而非配置开关,必须使用预编译语句。
- 先判断:检查项目中是否存在动态拼接 SQL 的代码。
- 优先做:将所有查询改为参数化查询(PreparedStatement)。
- 再验证:通过安全扫描或手动测试确认漏洞已修复。
核心原理与配置说明
这不是一个改配置文件的任务,而是一个代码审查和重构的过程。Spring Boot 默认配置中不存在专门用于防御 SQL 注入的属性开关。
1. 依赖配置
为了在 Controller 层进行有效的输入验证,建议引入验证依赖。在 pom.xml 中添加:
<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-validation</artifactId>\n</dependency>2. 应用配置文件
在 application.properties 或 application.yml 中,没有特定的 SQL 注入防御配置项。重点应放在数据库连接账号的权限控制上,确保应用账号仅拥有最小必要权限(如禁止 DROP、ALTER 权限)。
代码修复实战
重点检查所有涉及数据库查询的地方,确保用户输入没有直接拼接到 SQL 语句中。
1. Spring Data JPA 查询
避免在 @Query 注解中拼接字符串。错误写法是将变量直接连入 SQL 字符串,正确写法是使用 ?1 或 :param 占位符。
错误示例:@Query("SELECT u FROM User u WHERE u.name = '" + name + "'")
正确示例:@Query("SELECT u FROM User u WHERE u.name = ?1")
2. MyBatis XML 配置
在 MyBatis 中,严格区分 #{} 和 ${}。#{} 会进行预编译处理,是安全的;${} 是直接字符串替换,存在注入风险。除非是动态表名等无法使用占位符的特殊场景,否则一律使用 #{} 。
正确示例:<select id="findUser" resultType="User"> SELECT * FROM users WHERE id = #{id} </select>
3. Controller 层输入验证
在 Controller 层使用 Hibernate Validator 对输入参数进行格式校验,减少非法字符进入业务层的机会。在参数前添加 @Valid 或 @NotNull 等注解。
DTO 示例:
public class UserQueryDTO {\n @NotBlank(message = "用户名不能为空")\n private String name;\n // getters and setters\n}Controller 示例:
@PostMapping("/search")\npublic ResponseEntity<List<User>> search(@Valid @RequestBody UserQueryDTO dto) {\n // 业务逻辑\n}验证方法(仅限测试环境)
警告:严禁在生产环境进行注入测试,以下操作必须在隔离的测试环境中进行。
1. 手动测试
在测试环境的登录框或搜索框中输入典型的注入 Payload,例如 ' OR '1'='1。如果系统返回错误信息或绕过了验证,说明存在漏洞;如果系统提示无结果或输入非法,则可能已防御。
2. 代码扫描
使用静态代码分析工具(如 SonarQube)扫描项目,配置 SQL 注入规则集。查看是否有报告指出存在字符串拼接 SQL 的情况。
3. 动态扫描
使用 OWASP ZAP 等安全测试工具对运行中的项目进行扫描,观察是否有 SQL 注入相关的报警。
常见坑与排查
1. 模糊查询的拼接
在做 LIKE 查询时,容易写成 "%" + keyword + "%" 拼接到 SQL 中。正确做法是在 Java 代码中处理好百分号,然后传给占位符,例如 param = "%" + keyword + "%",SQL 中仍写 LIKE ?1。
2. 排序字段动态化
ORDER BY 后面的字段名通常不能使用占位符。如果允许用户指定排序字段,必须在代码中维护一个白名单,只允许传入预定义的字段名,不要直接使用用户输入。
3. 批量导入功能
批量插入数据时,为了性能有时会拼接多条 VALUES 语句。这种情况下需要严格校验每一条数据的合法性,或者使用 JDBC 的批量提交功能,而不是字符串拼接。
参考来源
- OWASP, SQL Injection Prevention Cheat Sheet, https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
- Spring Framework, Spring Data JPA - Query Methods, https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
- MyBatis Documentation, Dynamic SQL, https://mybatis.org/mybatis-3/dynamic-sql.html