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参数绑定方式
-
位置参数:
Query query = session.createQuery("from User where name = ?1", User.class); query.setParameter(1, parameter); -
命名参数:
Query query = session.createQuery("from User where name = :name", User.class); query.setParameter("name", parameter); -
命名参数列表:
List<String> names = Arrays.asList("WuXie","WangPangzi"); Query query = session.createQuery("from User where name in :names", User.class); query.setParameter("names", names); -
类实例参数:
parameter.setName("WuXie"); Query query = session.createQuery("from User where name = :name", User.class); query.setProperties(parameter);
三、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注入漏洞,保障应用数据安全。