java之sql注入代码审计
字数 1172 2025-08-22 12:22:24

Java SQL注入代码审计与MyBatis安全实践

1. JDBC基础与SQL注入原理

1.1 JDBC核心组件

JDBC (Java Database Connectivity) 是Java连接数据库的标准API,主要包含以下核心对象:

  1. DriverManager:管理数据库驱动注册
  2. Connection:代表数据库连接
  3. Statement:执行静态SQL语句
  4. PreparedStatement:执行预编译SQL语句
  5. ResultSet:存储查询结果集

1.2 Statement与SQL注入

不安全示例

String id = request.getParameter("id");
String sql = "select * from users where id = '"+id+"'";
Statement stmt = connection.createStatement();
ResultSet rs = stmt.executeQuery(sql);

问题分析

  • 直接拼接用户输入到SQL语句中
  • 攻击者可构造恶意输入如 1' or '1'='1 绕过验证
  • 执行流程:拼接SQL → 编译执行 → 返回结果

1.3 PreparedStatement防御机制

安全示例

String sql = "select * from users where id = ?";
PreparedStatement stmt = connection.prepareStatement(sql);
stmt.setString(1, id);
ResultSet rs = stmt.executeQuery();

安全原理

  1. 预编译阶段:SQL模板被编译
  2. 参数绑定阶段:用户输入作为纯数据处理
  3. 执行阶段:无法改变SQL结构

2. JDBC特殊场景下的SQL注入

2.1 IN语句注入

不安全实现

String delIds = "1, 2, 3"; // 用户可控输入
String sql = "DELETE FROM users WHERE id IN (" + delIds + ")";

安全解决方案

// 构建动态占位符
StringBuilder placeholders = new StringBuilder();
for(int i=0; i<delIds.size(); i++) {
    if(i>0) placeholders.append(",");
    placeholders.append("?");
}

String sql = "DELETE FROM users WHERE id IN ("+placeholders.toString()+")";
PreparedStatement pstmt = connection.prepareStatement(sql);

// 绑定参数
for(int i=0; i<delIds.size(); i++) {
    pstmt.setInt(i+1, delIds.get(i));
}

2.2 LIKE语句注入

不安全实现

String con = "admin%' or 1=1#";
String sql = "SELECT * FROM users WHERE password LIKE '%"+con+"%'";

安全解决方案

// 手动过滤通配符
pattern = pattern.replace("%", "\\%").replace("_", "\\_");

String sql = "SELECT * FROM users WHERE password LIKE ?";
PreparedStatement pstmt = connection.prepareStatement(sql);
pstmt.setString(1, "%"+pattern+"%");

2.3 ORDER BY注入

特殊性问题

  • ORDER BY后不能使用预编译占位符
  • 字段名不能加引号

安全解决方案

// 定义允许排序的字段白名单
private static final List<String> ALLOWED_SORT_COLUMNS = 
    Arrays.asList("id", "username", "password");

// 验证输入
if(!ALLOWED_SORT_COLUMNS.contains(sortOrder)) {
    throw new IllegalArgumentException("Invalid sort column");
}

String sql = "SELECT * FROM users ORDER BY " + sortOrder;
Statement stmt = connection.createStatement();

3. MyBatis框架安全实践

3.1 MyBatis架构概述

;

// 最终生成预编译SQL
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
// 得到:SELECT * FROM employee WHERE lastName = ?


### 3.4 ${}注入风险与防护

**风险示例**:
```xml
<select id="getUserByname" resultType="Employee">
    SELECT * FROM employee WHERE lastName = '${lastName}'
</select>

攻击方式

Employee params = new Employee();
params.setLastName("' OR '1'='1");
List<Employee> users = mapper.getUserByname(params);

防护建议

  1. 尽量使用#{}
  2. 必须使用${}时:
    • 实现参数白名单验证
    • 对特殊字符进行转义
    • 避免直接拼接用户输入

3.5 MyBatis注解方式安全

不安全示例

@Select("select * from category_ where name= '${name}' ")
public CategoryM getByName(@Param("name") String name);

安全写法

@Select("select * from category_ where name= #{name}")
public CategoryM getByName(@Param("name") String name);

4. 代码审计要点总结

4.1 危险API识别

  1. Statement直接拼接SQL
  2. MyBatis中${}的使用
  3. 动态SQL中的用户输入拼接
  4. 未过滤的ORDER BY、GROUP BY参数

4.2 安全开发建议

  1. 始终优先使用PreparedStatement
  2. MyBatis中优先使用#{}
  3. 必须动态拼接时:
    • 实现严格的白名单验证
    • 对特殊字符进行转义处理
    • 使用安全的SQL构建工具
  4. 定期进行代码安全审计

4.3 测试验证方法

  1. 输入特殊字符测试:'、"、\、--、#、/*
  2. 尝试逻辑绕过:or 1=1' or '1'='1
  3. 测试延时注入:sleep(5)
  4. 验证错误信息泄露

通过系统化的安全编码实践和严格的代码审计流程,可以有效预防Java应用中的SQL注入漏洞,保障数据安全。

Java SQL注入代码审计与MyBatis安全实践 1. JDBC基础与SQL注入原理 1.1 JDBC核心组件 JDBC (Java Database Connectivity) 是Java连接数据库的标准API,主要包含以下核心对象: DriverManager :管理数据库驱动注册 Connection :代表数据库连接 Statement :执行静态SQL语句 PreparedStatement :执行预编译SQL语句 ResultSet :存储查询结果集 1.2 Statement与SQL注入 不安全示例 : 问题分析 : 直接拼接用户输入到SQL语句中 攻击者可构造恶意输入如 1' or '1'='1 绕过验证 执行流程:拼接SQL → 编译执行 → 返回结果 1.3 PreparedStatement防御机制 安全示例 : 安全原理 : 预编译阶段:SQL模板被编译 参数绑定阶段:用户输入作为纯数据处理 执行阶段:无法改变SQL结构 2. JDBC特殊场景下的SQL注入 2.1 IN语句注入 不安全实现 : 安全解决方案 : 2.2 LIKE语句注入 不安全实现 : 安全解决方案 : 2.3 ORDER BY注入 特殊性问题 : ORDER BY后不能使用预编译占位符 字段名不能加引号 安全解决方案 : 3. MyBatis框架安全实践 3.1 MyBatis架构概述 ; // 最终生成预编译SQL BoundSql boundSql = sqlSource.getBoundSql(parameterObject); // 得到:SELECT * FROM employee WHERE lastName = ? 攻击方式 : 防护建议 : 尽量使用#{} 必须使用${}时: 实现参数白名单验证 对特殊字符进行转义 避免直接拼接用户输入 3.5 MyBatis注解方式安全 不安全示例 : 安全写法 : 4. 代码审计要点总结 4.1 危险API识别 Statement 直接拼接SQL MyBatis中${}的使用 动态SQL中的用户输入拼接 未过滤的ORDER BY、GROUP BY参数 4.2 安全开发建议 始终优先使用PreparedStatement MyBatis中优先使用#{} 必须动态拼接时: 实现严格的白名单验证 对特殊字符进行转义处理 使用安全的SQL构建工具 定期进行代码安全审计 4.3 测试验证方法 输入特殊字符测试: '、"、\、--、#、/* 等 尝试逻辑绕过: or 1=1 、 ' or '1'='1 测试延时注入: sleep(5) 验证错误信息泄露 通过系统化的安全编码实践和严格的代码审计流程,可以有效预防Java应用中的SQL注入漏洞,保障数据安全。