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数据库搭建

  1. 快速启动方式

    • 使用phpstudy集成环境
    • 包含PHP+MySQL+Apache
    • 支持多版本切换
  2. 使用Navicat连接

    • 连接后可执行SQL命令
    • 可查看和操作数据表

2.2 MySQL数据库配置

  1. 安装

    • 下载地址:https://dev.mysql.com/downloads/mysql/
    • 安装教程:https://www.runoob.com/mysql/mysql-install.html
  2. 远程连接配置

    GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '密码' WITH GRANT OPTION;
    FLUSH PRIVILEGES;
    
  3. 开启日志记录

    • 修改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方式)

  1. Dao接口
public interface StudentDao {
    public List<Student> selectStudent(@Param("name") String name);
}
  1. 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注入的场景:

  1. LIKE模糊查询使用${}
  2. IN查询使用${}
  3. ORDER BY/GROUP BY使用${}

8. 综合案例与审计技巧

8.1 漏洞审计技巧

  1. 关注点

    • 用户可控的前端参数
    • 未做安全处理直接拼接SQL
  2. 常见场景

    • JDBC中使用"+"拼接
    • MyBatis中使用${}
    • Hibernate中使用"+"拼接
  3. 特殊形式

    • 将前端参数作为SQL语句字段名而非值

8.2 修复建议

  1. 白名单方案

    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
    
  2. 预编译优先

    • 尽可能使用#{}
    • 必须使用${}时,严格过滤或使用白名单
  3. 特殊关键字处理

    • LIKE: 使用concat()函数
    • IN: 使用<foreach>标签
    • ORDER BY/GROUP BY: 白名单验证字段名

8.3 实际案例(OFCMS 1.1.3)

  1. 漏洞位置

    • 代码生成功能
    • 直接执行用户输入的SQL语句
  2. 漏洞利用

    update of_cms_link set link_name=updatexml(1,concat(0x7e,(user())),0) where link_id = 4
    
  3. 问题代码

    String sql = getPara("sql");
    Db.update(sql); // 直接执行未过滤的用户输入
    
  4. 根本原因

    • 虽然使用了预编译接口
    • 但执行的是完整SQL语句,预编译形同虚设

9. 防御措施总结

  1. 优先使用预编译

    • JDBC: PreparedStatement
    • MyBatis: #{}
  2. 必须拼接时的防护

    • 严格输入验证
    • 使用白名单机制
    • 特殊字符过滤
  3. 框架使用规范

    • 避免使用${}
    • 特殊场景使用安全写法
  4. 安全开发实践

    • 代码审计
    • 安全测试
    • 持续教育
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 远程连接配置 开启日志记录 修改my.ini配置文件 在[ mysqld ]下添加: 3. 在线SQL练习平台 | 平台 | 特点 | |------|------| | SQL Fiddle | 支持多种SQL引擎,可在线测试 | | SQLZOO | 包含教程和练习,支持多数据库 | | SQL Bolt | 适合初学者,由浅入深学习SQL | 4. SQL语法学习 4.1 SELECT查询 4.2 WHERE语句 4.3 UNION联合查询 合并多个SELECT结果集 要求:列数相同、数据类型相似、列顺序相同 4.4 ORDER BY关键字 4.5 LIKE模糊查询 4.6 IN关键字 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 万能密码绕过 5.2 常见注入点 认证页面 搜索框 POST/GET参数 HTTP头部 Cookie 5.3 数据库特性 information_ schema.schemata - 存储数据库名 information_ schema.tables - 存储表名 information_ schema.columns - 存储列名 6. SQL注入审计 6.1 Statement方式 风险 :直接拼接用户输入,易导致SQL注入 6.2 PreparedStatement(预编译) 优点 :使用参数化查询,防止SQL注入 6.3 LIKE模糊查询问题 6.4 ORDER BY问题 不能直接使用预编译处理 解决方案:使用白名单验证字段名 7. MyBatis框架注入问题 7.1 MyBatis简介 持久层框架,支持定制化SQL 避免大部分JDBC代码 可使用XML或注解配置 7.2 基本使用(XML方式) Dao接口 Mapper XML 7.3 MyBatis注入问题 1) ${}与#{}区别 ${param} - 直接拼接,存在注入风险 #{param} - 预编译处理,安全 2) 高危场景 LIKE模糊查询 IN查询 ORDER BY/GROUP BY 解决方案 :使用白名单验证字段名 7.4 总结 容易产生SQL注入的场景: LIKE模糊查询使用 ${} IN查询使用 ${} ORDER BY/GROUP BY使用 ${} 8. 综合案例与审计技巧 8.1 漏洞审计技巧 关注点 : 用户可控的前端参数 未做安全处理直接拼接SQL 常见场景 : JDBC中使用"+"拼接 MyBatis中使用 ${} Hibernate中使用"+"拼接 特殊形式 : 将前端参数作为SQL语句字段名而非值 8.2 修复建议 白名单方案 : 预编译优先 : 尽可能使用 #{} 必须使用 ${} 时,严格过滤或使用白名单 特殊关键字处理 : LIKE: 使用 concat() 函数 IN: 使用 <foreach> 标签 ORDER BY/GROUP BY: 白名单验证字段名 8.3 实际案例(OFCMS 1.1.3) 漏洞位置 : 代码生成功能 直接执行用户输入的SQL语句 漏洞利用 : 问题代码 : 根本原因 : 虽然使用了预编译接口 但执行的是完整SQL语句,预编译形同虚设 9. 防御措施总结 优先使用预编译 JDBC: PreparedStatement MyBatis: #{} 必须拼接时的防护 严格输入验证 使用白名单机制 特殊字符过滤 框架使用规范 避免使用 ${} 特殊场景使用安全写法 安全开发实践 代码审计 安全测试 持续教育