如何改造Java字符串拼接SQL代码以防范SQL注入漏洞?
解决Java项目SQL注入漏洞:参数化查询落地方案
你的核心问题是把字符串拼接生成SQL的逻辑改成参数化查询,核心思路是将用户输入的动态值从SQL模板中剥离,用占位符代替,再通过安全的方式将参数绑定到查询中。以下是针对你提到的代码结构(Controller层getSearchResults、SQL构建方法getSearchQuery/buildSQLWhereClause)的具体改写方案:
一、重构SQL构建逻辑:分离SQL模板与参数列表
原来的buildSQLWhereClause直接拼接用户输入到SQL片段里,现在要改成生成带占位符的SQL片段,同时收集对应的参数值。
示例改写(原生Java)
// 改写buildSQLWhereClause:返回带占位符的SQL片段 + 参数列表 private Pair<String, List<Object>> buildSQLWhereClause(String username, String status) { StringBuilder whereFragment = new StringBuilder(); List<Object> params = new ArrayList<>(); if (username != null && !username.isBlank()) { whereFragment.append(" AND username = ?"); params.add(username); } if (status != null && !status.isBlank()) { whereFragment.append(" AND status = ?"); params.add(status); } return new Pair<>(whereFragment.toString(), params); } // 改写getSearchQuery:组合基础SQL与动态片段,返回完整SQL模板和参数 private Pair<String, List<Object>> getSearchQuery(String username, String status) { String baseSql = "SELECT id, username, status, email FROM users WHERE 1=1"; Pair<String, List<Object>> wherePart = buildSQLWhereClause(username, status); String fullSql = baseSql + wherePart.getFirst(); return new Pair<>(fullSql, wherePart.getSecond()); }
注:如果项目没有Pair类,可以自定义一个简单的SqlAndParams类来封装SQL字符串和参数列表。
二、在数据访问层执行参数化查询
根据项目使用的技术栈,选择对应的参数化执行方式:
1. 原生JDBC + PreparedStatement
直接使用JDBC的PreparedStatement绑定参数,避免SQL注入:
public List<User> getSearchResults(String username, String status) throws SQLException { Pair<String, List<Object>> sqlAndParams = getSearchQuery(username, status); String sql = sqlAndParams.getFirst(); List<Object> params = sqlAndParams.getSecond(); // 用try-with-resources自动关闭资源 try (Connection conn = getDatabaseConnection()) { try (PreparedStatement pstmt = conn.prepareStatement(sql)) { // 绑定参数:索引从1开始 for (int i = 0; i < params.size(); i++) { pstmt.setObject(i + 1, params.get(i)); } try (ResultSet rs = pstmt.executeQuery()) { List<User> results = new ArrayList<>(); while (rs.next()) { // 映射ResultSet到实体类 User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setStatus(rs.getString("status")); user.setEmail(rs.getString("email")); results.add(user); } return results; } } } }
2. Spring JdbcTemplate(Spring项目推荐)
Spring的JdbcTemplate已经封装了PreparedStatement的细节,代码更简洁:
@Autowired private JdbcTemplate jdbcTemplate; public List<User> getSearchResults(String username, String status) { Pair<String, List<Object>> sqlAndParams = getSearchQuery(username, status); String sql = sqlAndParams.getFirst(); Object[] params = sqlAndParams.getSecond().toArray(); // 用RowMapper映射结果 return jdbcTemplate.query(sql, params, (rs, rowNum) -> { User user = new User(); user.setId(rs.getInt("id")); user.setUsername(rs.getString("username")); user.setStatus(rs.getString("status")); user.setEmail(rs.getString("email")); return user; }); }
3. MyBatis(ORM框架场景)
如果项目用MyBatis,直接用#{}占位符代替字符串拼接,框架会自动处理参数化:
Mapper接口
public interface UserMapper { List<User> searchUsers(@Param("username") String username, @Param("status") String status); }
XML映射文件
<select id="searchUsers" resultType="com.yourpackage.User"> SELECT id, username, status, email FROM users WHERE 1=1 <if test="username != null and username != ''"> AND username = #{username} </if> <if test="status != null and status != ''"> AND status = #{status} </if> </select>
注意:这里用#{}而不是${},${}会直接拼接字符串,依然存在注入风险。
三、特殊场景处理:动态表名/列名
如果你的SQL拼接涉及动态表名或列名(比如用户指定排序字段),这类场景不能用占位符,必须做白名单校验:
// 校验列名是否在允许的白名单内 private boolean isValidSortColumn(String column) { Set<String> allowedColumns = Set.of("id", "username", "status", "create_time"); return allowedColumns.contains(column.toLowerCase()); } // 合法后再拼入SQL public String getSortedSearchSql(String sortColumn) { String baseSql = getSearchQuery(null, null).getFirst(); if (isValidSortColumn(sortColumn)) { return baseSql + " ORDER BY " + sortColumn; } // 默认排序 return baseSql + " ORDER BY create_time DESC"; }
关键原则
- 所有来自用户/外部系统的输入,绝对不能直接拼入SQL字符串;
- 优先使用ORM框架(MyBatis、JPA)的参数化查询能力,减少手动编写JDBC代码的出错概率;
- 禁止使用
Statement执行拼接后的SQL,必须用PreparedStatement或等效的参数化查询方式。
内容的提问来源于stack exchange,提问作者femalesoftwareengineer
相关产品推荐
相关产品推荐

