Java审计之SQL注入
字数 1994 2025-08-13 21:33:17

Java审计之SQL注入漏洞详解与防御

一、SQL注入基础概念

1.1 定义

SQL注入(SQL Injection)是指当Web应用程序未对用户可控输入(如参数、表单、Cookie等)进行规范性校验、过滤和数据清洗时,攻击者通过构造特殊payload,将恶意SQL代码拼接到应用程序的SQL语句中,导致数据库执行非预期的SQL命令。

1.2 SQL注入特点

  • 广泛性:任何基于SQL语言的数据库都可能遭受攻击
  • 隐蔽性:SQL语句嵌入在HTTP请求中,难以与正常语句区分
  • 危害大:可获取数据库敏感信息、提权,甚至控制整个系统
  • 操作方便:存在大量自动化工具如SQLmap等

二、Java中SQL注入产生场景

2.1 JDBC使用不当导致的SQL注入

2.1.1 Statement直接拼接

String sql = "select * from user where id ="+req.getParameter("id");
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);

风险:直接拼接用户输入,攻击者可构造id=1 or 1=1等payload改变SQL语义

2.1.2 PreparedStatement错误使用

String sql = "select * from user where id ="+req.getParameter("id");
PreparedStatement pst = conn.prepareStatement(sql);
ResultSet rs = pst.executeQuery();

风险:虽然使用了PreparedStatement,但仍采用拼接方式,预编译无法阻止SQL注入

2.1.3 PreparedStatement正确用法

String sql = "select * from user where id = ?";
PreparedStatement pstt = conn.prepareStatement(sql);
pstt.setInt(1, Integer.parseInt(req.getParameter("id")));
ResultSet rs = pstt.executeQuery();

优点:使用占位符?并严格类型检查,有效防止SQL注入

2.2 框架使用不当导致的SQL注入

2.2.1 MyBatis框架

MyBatis核心组件
  • Configuration:保存所有配置信息
  • SqlSession:与数据库交互的会话
  • Executor:SQL执行核心,负责生成SQL和维护缓存
  • StatementHandler:封装JDBC Statement操作
  • ParameterHandler:参数类型转换
  • ResultSetHandler:结果集转换
  • TypeHandler:Java与JDBC类型映射
  • MappedStatement:维护SQL节点封装
  • SqlSource:动态生成SQL语句
  • BoundSql:动态SQL及参数信息
#{}${}区别
  • #{}:占位符,预编译时自动添加引号,安全
  • ${}:SQL拼接符号,直接替换不添加引号,存在SQL注入风险
风险代码示例
<select id="QueryByName" parameterType="String" resultType="com.demo.bean.User">
    select id, name, age from user where name = '${name}'
</select>

风险:输入WuXie' or 1=1 --+123可导致注入

安全代码示例
<select id="QueryByName" parameterType="String" resultType="com.demo.bean.User">
    select * from user where name = #{name}
</select>

优点:使用#{}预编译,防止SQL注入

2.2.2 Hibernate框架

HQL与SQL区别
特性 HQL SQL
查询对象 类和属性 表和列
大小写 关键字不区分,其他区分 不区分
占位符 命名参数或?(Hibernate5后不支持) ?
参数绑定 支持命名参数 不支持
查询语言 面向对象 面向结构
ORM概念

对象关系映射(Object Relation Mapping),实现面向对象语言与关系数据库之间的数据转换

风险代码示例
Query query = session.createNativeQuery("select * FROM User where name='" + parameter + "'");
List user = query.getResultList();

风险:直接拼接SQL语句

安全代码示例
Query query = session.createQuery("from com.demo.bean.User where name = ?1", User.class);
query.setParameter(1, parameter);
List user = query.getResultList();

优点:使用参数绑定,预编译SQL

HQL参数绑定方式
  1. 位置参数

    Query query = session.createQuery("from User where name = ?1", User.class);
    query.setParameter(1, parameter);
    
  2. 命名参数

    Query query = session.createQuery("from User where name = :name", User.class);
    query.setParameter("name", parameter);
    
  3. 命名参数列表

    List<String> names = Arrays.asList("WuXie","WangPangzi");
    Query query = session.createQuery("from User where name in :names", User.class);
    query.setParameter("names", names);
    
  4. 类实例参数

    parameter.setName("WuXie");
    Query query = session.createQuery("from User where name = :name", User.class);
    query.setProperties(parameter);
    

三、SQL注入防御措施

3.1 通用防御原则

  1. 使用预编译语句:优先使用PreparedStatement或框架提供的参数化查询
  2. 严格输入验证:对用户输入进行白名单验证
  3. 最小权限原则:数据库账户使用最小必要权限
  4. 避免拼接SQL:特别是表名、字段名等动态部分

3.2 特定场景防御

  1. MyBatis防御

    • 优先使用#{}
    • 必须使用${}时,手动过滤危险字符
    • 对order by等动态部分进行白名单校验
  2. Hibernate防御

    • 使用HQL参数绑定而非拼接
    • 对原生SQL使用预编译
    • 避免直接使用用户输入构造查询
  3. JDBC防御

    • 使用PreparedStatement并正确设置参数
    • 对数值类型使用setInt等严格类型方法
    • 避免直接拼接SQL语句

3.3 其他防御措施

  1. 使用ORM框架的安全特性:如Hibernate的Criteria API
  2. Web应用防火墙(WAF):作为辅助防御层
  3. 定期安全审计:检查代码中的SQL注入风险点
  4. 错误信息处理:避免泄露数据库结构信息

四、总结

SQL注入是Web应用中最常见且危险的漏洞之一,Java应用中无论是直接使用JDBC还是通过MyBatis、Hibernate等框架操作数据库,都可能因不当使用而产生SQL注入风险。关键在于:

  1. 理解各技术中SQL注入的产生原理
  2. 掌握正确的参数化查询使用方法
  3. 对必须使用动态SQL的场景进行严格过滤
  4. 遵循安全编码规范,建立防御性编程思维

通过本文介绍的各种安全编码实践,开发者可以有效预防Java应用中的SQL注入漏洞,保障应用数据安全。

Java审计之SQL注入漏洞详解与防御 一、SQL注入基础概念 1.1 定义 SQL注入(SQL Injection)是指当Web应用程序未对用户可控输入(如参数、表单、Cookie等)进行规范性校验、过滤和数据清洗时,攻击者通过构造特殊payload,将恶意SQL代码拼接到应用程序的SQL语句中,导致数据库执行非预期的SQL命令。 1.2 SQL注入特点 广泛性 :任何基于SQL语言的数据库都可能遭受攻击 隐蔽性 :SQL语句嵌入在HTTP请求中,难以与正常语句区分 危害大 :可获取数据库敏感信息、提权,甚至控制整个系统 操作方便 :存在大量自动化工具如SQLmap等 二、Java中SQL注入产生场景 2.1 JDBC使用不当导致的SQL注入 2.1.1 Statement直接拼接 风险 :直接拼接用户输入,攻击者可构造 id=1 or 1=1 等payload改变SQL语义 2.1.2 PreparedStatement错误使用 风险 :虽然使用了PreparedStatement,但仍采用拼接方式,预编译无法阻止SQL注入 2.1.3 PreparedStatement正确用法 优点 :使用占位符 ? 并严格类型检查,有效防止SQL注入 2.2 框架使用不当导致的SQL注入 2.2.1 MyBatis框架 MyBatis核心组件 Configuration :保存所有配置信息 SqlSession :与数据库交互的会话 Executor :SQL执行核心,负责生成SQL和维护缓存 StatementHandler :封装JDBC Statement操作 ParameterHandler :参数类型转换 ResultSetHandler :结果集转换 TypeHandler :Java与JDBC类型映射 MappedStatement :维护SQL节点封装 SqlSource :动态生成SQL语句 BoundSql :动态SQL及参数信息 #{} 与 ${} 区别 #{} :占位符,预编译时自动添加引号,安全 ${} :SQL拼接符号,直接替换不添加引号,存在SQL注入风险 风险代码示例 风险 :输入 WuXie' or 1=1 --+123 可导致注入 安全代码示例 优点 :使用 #{} 预编译,防止SQL注入 2.2.2 Hibernate框架 HQL与SQL区别 | 特性 | HQL | SQL | |------|-----|-----| | 查询对象 | 类和属性 | 表和列 | | 大小写 | 关键字不区分,其他区分 | 不区分 | | 占位符 | 命名参数或?(Hibernate5后不支持) | ? | | 参数绑定 | 支持命名参数 | 不支持 | | 查询语言 | 面向对象 | 面向结构 | ORM概念 对象关系映射(Object Relation Mapping),实现面向对象语言与关系数据库之间的数据转换 风险代码示例 风险 :直接拼接SQL语句 安全代码示例 优点 :使用参数绑定,预编译SQL HQL参数绑定方式 位置参数 : 命名参数 : 命名参数列表 : 类实例参数 : 三、SQL注入防御措施 3.1 通用防御原则 使用预编译语句 :优先使用PreparedStatement或框架提供的参数化查询 严格输入验证 :对用户输入进行白名单验证 最小权限原则 :数据库账户使用最小必要权限 避免拼接SQL :特别是表名、字段名等动态部分 3.2 特定场景防御 MyBatis防御 : 优先使用 #{} 必须使用 ${} 时,手动过滤危险字符 对order by等动态部分进行白名单校验 Hibernate防御 : 使用HQL参数绑定而非拼接 对原生SQL使用预编译 避免直接使用用户输入构造查询 JDBC防御 : 使用PreparedStatement并正确设置参数 对数值类型使用setInt等严格类型方法 避免直接拼接SQL语句 3.3 其他防御措施 使用ORM框架的安全特性 :如Hibernate的Criteria API Web应用防火墙(WAF) :作为辅助防御层 定期安全审计 :检查代码中的SQL注入风险点 错误信息处理 :避免泄露数据库结构信息 四、总结 SQL注入是Web应用中最常见且危险的漏洞之一,Java应用中无论是直接使用JDBC还是通过MyBatis、Hibernate等框架操作数据库,都可能因不当使用而产生SQL注入风险。关键在于: 理解各技术中SQL注入的产生原理 掌握正确的参数化查询使用方法 对必须使用动态SQL的场景进行严格过滤 遵循安全编码规范,建立防御性编程思维 通过本文介绍的各种安全编码实践,开发者可以有效预防Java应用中的SQL注入漏洞,保障应用数据安全。