Java代码审计-SQL注入全解
字数 3006 2025-08-11 21:26:35
Java代码审计-SQL注入全解
1. 基础理论介绍
1.1 数据库类型
关系型数据库
- 采用关系模型(二维表格模型)组织数据
- 由二维表及其之间的联系组成的数据组织
- 代表产品:MySQL、SQL Server、Oracle、Access
非关系型数据库(NoSQL)
- 非关系型、分布式、不保证遵循ACID原则
- 键值对存储,结构不固定
- 代表产品:Redis、Memcached、MongoDB、HBase
1.2 常用关系型数据库
| 数据库 | 特点 |
|---|---|
| MySQL | 开源关系数据库,现属Oracle旗下 |
| SQL Server | 微软开发,支持Windows平台 |
| Access | 微软小型数据库,常用于ASP+IIS环境 |
| Oracle | 第一个开放式商品化关系型数据库管理系统 |
1.3 常用非关系型数据库
| 数据库 | 特点 |
|---|---|
| Redis | 开源键值存储,支持复制、事务等 |
| Memcached | 分布式内存缓存系统,加速Web应用 |
| MongoDB | 面向文档的NoSQL数据库,使用JSON格式 |
| HBase | 分布式非关系数据库,基于Google BigTable |
1.4 二者区别与联系
| 特性 | 关系型数据库 | 非关系型数据库 |
|---|---|---|
| 事务一致性 | 强 | 弱或不保证 |
| 读写性能 | 相对较低 | 高性能 |
| 适用场景 | 银行等高一致性系统 | 海量数据处理 |
| 安全性 | 高 | 相对较低 |
2. 基础环境搭建
2.1 MySQL数据库搭建
-
快速启动方式
- 使用phpstudy集成环境
- 包含PHP+MySQL+Apache
- 支持多版本切换
-
使用Navicat连接
- 连接后可执行SQL命令
- 可查看和操作数据表
2.2 MySQL数据库配置
-
安装
- 下载地址:https://dev.mysql.com/downloads/mysql/
- 安装教程:https://www.runoob.com/mysql/mysql-install.html
-
远程连接配置
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '密码' WITH GRANT OPTION; FLUSH PRIVILEGES; -
开启日志记录
- 修改my.ini配置文件
- 在[mysqld]下添加:
log="C:/path/to/mysql_log.txt"
3. 在线SQL练习平台
| 平台 | 特点 |
|---|---|
| SQL Fiddle | 支持多种SQL引擎,可在线测试 |
| SQLZOO | 包含教程和练习,支持多数据库 |
| SQL Bolt | 适合初学者,由浅入深学习SQL |
4. SQL语法学习
4.1 SELECT查询
SELECT column_name FROM table_name;
SELECT * FROM table_name;
4.2 WHERE语句
SELECT column_name FROM table_name WHERE column_name operator value;
4.3 UNION联合查询
- 合并多个SELECT结果集
- 要求:列数相同、数据类型相似、列顺序相同
4.4 ORDER BY关键字
SELECT column_name FROM table_name ORDER BY column_name ASC|DESC;
4.5 LIKE模糊查询
SELECT column_name FROM table_name WHERE column_name LIKE pattern;
-- 示例:查找包含"张"的记录
SELECT * FROM student_table WHERE student_name LIKE '%张%';
4.6 IN关键字
SELECT column_name FROM table_name WHERE column_name IN (value1, value2,...);
4.7 GROUP BY分组查询
- 常与聚合函数配合使用
- 常用聚合函数:COUNT(), SUM(), AVG(), MAX(), MIN()
4.8 数据库常见函数
1) 聚合函数
- AVG() - 平均值
- COUNT() - 计数
- MAX()/MIN() - 最大/最小值
- SUM() - 求和
2) 字符串函数
- CONCAT() - 合并字符串
- LENGTH()/CHAR_LENGTH() - 获取长度
- UPPER()/LOWER() - 大小写转换
- SUBSTRING() - 获取子串
- TRIM() - 去空格
- REPLACE() - 字符串替换
3) 日期时间函数
- CURDATE()/CURRENT_DATE() - 当前日期
- CURTIME()/CURRENT_TIME() - 当前时间
- NOW() - 当前日期时间
- MONTH()/YEAR() - 获取月份/年份
- HOUR()/MINUTE() - 获取小时/分钟
4) 数值函数
- ABS() - 绝对值
- CEIL()/FLOOR() - 向上/向下取整
- MOD() - 取模
- ROUND() - 四舍五入
- TRUNCATE() - 数值截取
5) 安全相关函数
- USER()/CURRENT_USER() - 获取当前用户
- DATABASE()/SCHEMA() - 获取当前数据库
- @@HOSTNAME - 获取服务器主机名
5. SQL注入漏洞
5.1 万能密码绕过
-- 原始SQL
SELECT * FROM admin WHERE username='$username' AND password='$password';
-- 注入后
SELECT * FROM admin WHERE username='' OR 1=1 #' AND password='$password';
5.2 常见注入点
- 认证页面
- 搜索框
- POST/GET参数
- HTTP头部
- Cookie
5.3 数据库特性
- information_schema.schemata - 存储数据库名
- information_schema.tables - 存储表名
- information_schema.columns - 存储列名
6. SQL注入审计
6.1 Statement方式
// 存在SQL注入的代码示例
String name = sc.nextLine();
String sqlString = "select * from student_table where student_name = '"+name+"'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sqlString);
风险:直接拼接用户输入,易导致SQL注入
6.2 PreparedStatement(预编译)
// 安全代码示例
String sql = "select * from student_table where student_name = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
ResultSet rs = pstmt.executeQuery();
优点:使用参数化查询,防止SQL注入
6.3 LIKE模糊查询问题
// 错误的预编译方式 - 仍存在注入风险
PreparedStatement pstmt = conn.prepareStatement(sql+"'%"+name+"%'");
// 正确的预编译方式
String sql = "select * from student_table where student_name like concat('%',?,'%')";
6.4 ORDER BY问题
- 不能直接使用预编译处理
- 解决方案:使用白名单验证字段名
7. MyBatis框架注入问题
7.1 MyBatis简介
- 持久层框架,支持定制化SQL
- 避免大部分JDBC代码
- 可使用XML或注解配置
7.2 基本使用(XML方式)
- Dao接口
public interface StudentDao {
public List<Student> selectStudent(@Param("name") String name);
}
- Mapper XML
<select id="selectStudent" resultType="com.mybatis.pojo.Student">
select * from student_table where student_name=#{name}
</select>
7.3 MyBatis注入问题
1) ${}与#{}区别
${param}- 直接拼接,存在注入风险#{param}- 预编译处理,安全
2) 高危场景
LIKE模糊查询
<!-- 危险写法 -->
<select id="selectStudent" resultType="com.mybatis.pojo.Student">
select * from student_table where student_name like '%${name}%'
</select>
<!-- 安全写法 -->
<select id="selectStudent" resultType="com.mybatis.pojo.Student">
select * from student_table where student_name like concat('%',#{name},'%')
</select>
IN查询
<!-- 危险写法 -->
<select id="selectStudent" resultType="com.mybatis.pojo.Student">
select * from student_table where student_id in (${name})
</select>
<!-- 安全写法 -->
<select id="selectStudent" resultType="com.mybatis.pojo.Student">
select * from student_table where student_id in
<foreach collection="list" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
ORDER BY/GROUP BY
<!-- 危险写法 -->
<select id="selectStudent" resultType="com.mybatis.pojo.Student">
select * from student_table order by ${name}
</select>
解决方案:使用白名单验证字段名
7.4 总结
容易产生SQL注入的场景:
- LIKE模糊查询使用
${} - IN查询使用
${} - ORDER BY/GROUP BY使用
${}
8. 综合案例与审计技巧
8.1 漏洞审计技巧
-
关注点:
- 用户可控的前端参数
- 未做安全处理直接拼接SQL
-
常见场景:
- JDBC中使用"+"拼接
- MyBatis中使用
${} - Hibernate中使用"+"拼接
-
特殊形式:
- 将前端参数作为SQL语句字段名而非值
8.2 修复建议
-
白名单方案:
Map<String, String> map = new HashMap<>(); map.put("id", "id"); map.put("name", "name"); String param = request.getParameter("param"); String safeValue = map.get(param); // 不在白名单则返回null -
预编译优先:
- 尽可能使用
#{} - 必须使用
${}时,严格过滤或使用白名单
- 尽可能使用
-
特殊关键字处理:
- LIKE: 使用
concat()函数 - IN: 使用
<foreach>标签 - ORDER BY/GROUP BY: 白名单验证字段名
- LIKE: 使用
8.3 实际案例(OFCMS 1.1.3)
-
漏洞位置:
- 代码生成功能
- 直接执行用户输入的SQL语句
-
漏洞利用:
update of_cms_link set link_name=updatexml(1,concat(0x7e,(user())),0) where link_id = 4 -
问题代码:
String sql = getPara("sql"); Db.update(sql); // 直接执行未过滤的用户输入 -
根本原因:
- 虽然使用了预编译接口
- 但执行的是完整SQL语句,预编译形同虚设
9. 防御措施总结
-
优先使用预编译
- JDBC: PreparedStatement
- MyBatis: #{}
-
必须拼接时的防护
- 严格输入验证
- 使用白名单机制
- 特殊字符过滤
-
框架使用规范
- 避免使用
${} - 特殊场景使用安全写法
- 避免使用
-
安全开发实践
- 代码审计
- 安全测试
- 持续教育