如何修复Spring JDBC中JdbcTemplate.query的SQL注入SonarQube问题?
修复Spring JDBC的SQL注入问题(SonarQube告警)
我来帮你理清这个问题——你遇到的核心问题是把占位符?放在了单引号里面,导致它被当成了普通字符串,而不是预编译语句的参数占位符。咱们一步步来修复:
1. 你的错误原因
当你写date('?')时,SQL解析器会把'?'当成一个字面量字符串(也就是数据库会直接查找日期等于'?'的数据),而不是预编译的参数占位符。JdbcTemplate根本不会识别这个位置需要替换参数,自然达不到防注入的效果,还会导致查询逻辑错误。
2. 正确的占位符写法
预编译占位符?不需要加单引号,JdbcTemplate会根据你传入参数的类型(比如日期、字符串)自动处理格式转换和安全注入。你只需要把占位符直接作为SQL函数的参数即可:
StringBuilder query = new StringBuilder(); query.append("SELECT * from Events EVNT") .append(" WHERE EVNT.Roll IN ('5', '1') AND EVNTDemo.Roll is null ") .append(" AND date(EVNT.Updt) between date(?) AND CURRENT DATE - 10 DAY"); // 注意:要把StringBuilder转成String传入query方法 final Object[] args = new Object[]{getTMyDate()}; result = jdbcTemplate.query(query.toString(), args, new MyMapper());
这里的关键改动:
- 去掉了
?周围的单引号,改成date(?) - 显式调用
query.toString(),确保传入的是完整的SQL字符串(部分JdbcTemplate版本可以直接接收StringBuilder,但显式转换更稳妥)
3. 其他修复SQL注入的方法
除了基础的?占位符,还有几种更优雅、更安全的方案:
a. 使用NamedParameterJdbcTemplate(推荐,可读性更高)
如果你的SQL有多个参数,用命名参数代替?可以避免参数顺序搞错的问题,代码可读性也更强:
// 用多行字符串写法更清晰(Java 15+支持) String sql = """ SELECT * from Events EVNT WHERE EVNT.Roll IN ('5', '1') AND EVNTDemo.Roll is null AND date(EVNT.Updt) between date(:startDate) AND CURRENT DATE - 10 DAY """; Map<String, Object> params = new HashMap<>(); params.put("startDate", getTMyDate()); // 基于现有jdbcTemplate创建NamedParameterJdbcTemplate NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate); result = namedJdbcTemplate.query(sql, params, new MyMapper());
b. 处理动态IN子句(如果IN列表是动态的)
如果未来EVNT.Roll IN (...)里的值不是固定的'5','1',而是动态生成的,绝对不能直接拼接字符串。可以用NamedParameterJdbcTemplate直接传入列表参数:
// 假设rollValues是动态获取的列表 List<String> rollValues = Arrays.asList("5", "1"); String sql = """ SELECT * from Events EVNT WHERE EVNT.Roll IN (:rolls) AND EVNTDemo.Roll is null AND date(EVNT.Updt) between date(:startDate) AND CURRENT DATE - 10 DAY """; Map<String, Object> params = new HashMap<>(); params.put("rolls", rollValues); params.put("startDate", getTMyDate()); NamedParameterJdbcTemplate namedJdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate); result = namedJdbcTemplate.query(sql, params, new MyMapper());
c. 使用Spring Data JPA(如果项目兼容)
如果你的项目已经引入了Spring Data JPA,直接写Repository方法即可,框架会自动处理预编译和防注入,完全不用手动拼接SQL:
public interface EventRepository extends JpaRepository<Event, Long> { @Query("SELECT evnt FROM Event evnt WHERE evnt.roll IN (:rolls) AND evnt.demoRoll IS NULL " + "AND FUNCTION('date', evnt.updt) BETWEEN FUNCTION('date', :startDate) AND CURRENT_DATE - 10") List<Event> findEventsByRollsAndDateRange(@Param("rolls") List<String> rolls, @Param("startDate") Date startDate); }
最后再强调一遍核心原则:永远不要用字符串拼接的方式把动态值(哪怕是你自己生成的)直接插入SQL语句,所有动态值都必须通过预编译占位符传入,这是防止SQL注入的根本方法。
内容的提问来源于stack exchange,提问作者GKr297
相关产品推荐
相关产品推荐

