【Java 代码审计入门-02】SQL 漏洞原理与实际案例介绍
字数 883 2025-08-25 22:58:46
Java 代码审计入门:SQL 漏洞原理与实际案例
0x00 前言
本系列文章旨在为具备Java基本语法基础的学习者提供系统的Java代码审计教程。本文将重点讲解SQL注入漏洞的原理、实际案例及修复方案。
0x01 环境准备
数据库设置
CREATE DATABASE sec_sql CHARSET utf8;
CREATE TABLE `admin` (
`uid` int(11) NOT NULL AUTO_INCREMENT COMMENT 'uid',
`username` varchar(100) NOT NULL COMMENT '账号',
`password` varchar(100) NOT NULL COMMENT '密码',
PRIMARY KEY (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
CREATE TABLE `userinfo` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(100) NOT NULL COMMENT '名称',
`age` int(11) NOT NULL COMMENT '年龄',
`content` varchar(100) NOT NULL COMMENT '联系方式',
`address` varchar(255) NOT NULL COMMENT '家庭地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
测试项目结构
- Servlet层接收请求
- 调用UserInfoServiceImpl
- UserInfoServiceImpl调用UserInfoDaoImpl
- UserInfoDaoImpl操作数据库
- 返回UserInfo对象并最终显示到info.jsp
0x02 SQL注入原理
SQL注入是通过将恶意SQL命令插入应用程序的HTTP请求中,在服务器端被接收后参与数据库操作,最终欺骗服务器执行恶意SQL命令。
漏洞代码示例
public UserInfo UserInfoFoundDao(String id){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
UserInfo userinfo = null;
try{
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/sec_sql","root","admin888");
String sql = "select * from userinfo where id = " + id; // 直接拼接用户输入
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
// ... 结果处理
}
// ...
}
攻击示例
- 基础注入:
id=1 and 1=1 - 联合查询获取管理员凭证:
id=2 union select 1,2,3,group_concat(username),group_concat(password) from admin--
0x03 修复方案
1. 使用预编译
String sql = "select * from userinfo where id = ?";
ps = conn.prepareStatement(sql);
ps.setInt(1,id); // 参数化查询
注意:预编译并非绝对安全,以下情况仍需注意:
order by子句不能使用预编译like子句需要特殊处理in子句需要特殊处理
2. 修改数据类型
public UserInfo UserInfoFoundDao(int id){ // 使用int而非String
// ...
String sql = "select * from userinfo where id = " + id;
// ...
}
3. 其他防御措施
- 输入验证和过滤
- 最小权限原则
- 错误信息处理
- 使用ORM框架的安全方法
0x04 实际案例:CVE-2019-9615分析
案例介绍
OFCMS v1.1.3之前版本存在后台SQL注入漏洞,位于admin/system/generate/create?sql=路径。
漏洞代码
public void create() {
try {
String sql = getPara("sql"); // 直接获取用户输入
Db.update(sql); // 直接执行SQL
rendSuccessJson();
} catch (Exception e) {
e.printStackTrace();
rendFailedJson(ErrorCode.get("9999"), e.getMessage());
}
}
攻击示例
update of_cms_link set link_name=updatexml(1,concat(0x7e,(user())),0) where link_id = 4
修复建议
- 限制后台功能权限
- 重写业务功能,避免直接执行用户输入
- 过滤危险关键字(黑名单)
- 设置SQL操作白名单
0x05 总结
- Java中的SQL注入原理与PHP类似,都是由于未正确处理用户输入
- 预编译是主要防御手段,但需注意特殊情况
- 框架使用不当(如Mybatis的like/order by/in,Hibernate的createQuery)仍可能导致注入
- 实际审计中需关注数据流从用户输入到SQL语句的全过程