java之sql注入代码审计
字数 1172 2025-08-22 12:22:24
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注入
不安全示例:
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();
安全原理:
- 预编译阶段:SQL模板被编译
- 参数绑定阶段:用户输入作为纯数据处理
- 执行阶段:无法改变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);
防护建议:
- 尽量使用#{}
- 必须使用${}时:
- 实现参数白名单验证
- 对特殊字符进行转义
- 避免直接拼接用户输入
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识别
- 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注入漏洞,保障数据安全。