JAVA代码审计篇-SQL注入
字数 1244 2025-08-11 08:35:47
Java代码审计之SQL注入漏洞详解
1. SQL注入漏洞简介
SQL注入攻击是黑客利用SQL注入漏洞对数据库进行攻击的常用手段之一。攻击者通过浏览器或其他客户端将恶意SQL语句插入到网站参数中,网站应用程序未经过滤便将恶意SQL语句带入数据库执行。
危害影响
- 数据库信息泄露
- 数据被窃取
- 网页被篡改
- 网站被挂马
- 服务器被远程控制
- 被安装后门
分类
- 按输入类型:
- 数字型注入
- 字符串型注入
- 按攻击方式:
- 联合查找型注入
- 报错注入
- 时间盲注
- 布尔盲注
2. SQL注入的条件
- 输入用户可控
- 输入直接或间接拼入SQL语句执行
3. 审计方法
审计流程
- 根据
SELECT、UPDATE等SQL关键字定位代码片段 - 检查SQL语句中是否存在变量引用
- 跟踪变量是否可控
自动化工具
- Fortify等静态代码分析工具
Java语言特性
Java是强类型语言,审计时应:
- 找到所有包含SQL语句的点
- 观察传参类型是否为String类型(只有String类型才可能SQL注入)
4. Java中执行SQL的几种方式
4.1 使用JDBC的Statement
Statement是Java JDBC下执行SQL语句的原生方式,通过拼接执行SQL语句。
风险:若拼接未经过滤,将出现SQL注入漏洞。
示例代码:
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection(DBURL, DBUser, DBPassWord);
Statement state = conn.createStatement();
String sql = "SELECT * FROM user WHERE id=" + id;
state.executeQuery(sql);
4.2 使用JDBC的PreparedStatement
PreparedStatement是Statement的子接口,包含已编译的SQL语句,使用参数化查询。
优势:
- 预编译特性,执行更快
- 有效防止SQL注入
使用方法:
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection(DBURL, DBUser, DBPassWord);
String sql = "SELECT * FROM user WHERE id = ?";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, id);
ResultSet resultSet = preparedStatement.executeQuery();
4.3 使用MyBatis框架
MyBatis是一个Java持久化框架,通过XML或注解将对象与SQL语句关联。
两种使用方式:
- 注解方式:
@Select("select * from Blog where id = #{id}")
Blog selectBlog(int id);
- XML映射方式:
<select id="selectBlog" parameterType="int" resultType="Blog">
select * from Blog where id = #{id}
</select>
5. 常见SQL注入漏洞代码
5.1 SQL语句参数直接动态拼接
漏洞代码:
String id = request.getParameter("id");
res = st.executeQuery("SELECT * FROM \"IWEBSEC\".\"user\" WHERE \"id\"=" + id);
攻击示例:
http://www.any.com/index.jsp?id=1 union select null,null,SYS_CONTEXT('USERENV','CURRENT_USER') from dual
5.2 预编译有误
错误示例:
String username = "user%' or '1'='1'#";
String sql = "SELECT * FROM user where id = ?";
if (!CommonUtils.isEmptyStr(username))
sql += " and username like '%" + username + "%'";
问题:虽然id使用了参数化查询,但username仍使用拼接方式。
5.3 order by注入
特殊场景:order by子句不能使用参数化查询,必须拼接字段名。
漏洞代码:
String id = "2 or 1=1";
String sql = "SELECT * FROM user order by " + id;
防御:对order by字段进行严格过滤。
5.4 #和_模糊查询问题
风险:预编译查询不会对%和_进行转义,可能导致恶意模糊查询。
示例:
String username = "%user%";
// 会作为通配符处理,而非普通字符
6. 防御措施
- 优先使用PreparedStatement进行参数化查询
- 避免直接拼接SQL语句,特别是用户输入
- 对order by等特殊场景进行严格过滤
- 对模糊查询中的特殊字符进行转义处理
- 使用ORM框架如MyBatis时,确保正确使用参数化查询
- 输入验证:对用户输入进行严格验证和过滤
7. 最佳实践
- 在代码审查时重点关注SQL语句构建方式
- 使用自动化工具辅助检测潜在漏洞
- 对数据库操作进行统一封装,避免分散的SQL拼接
- 定期进行安全测试和渗透测试
- 保持框架和库的最新版本,及时修复已知漏洞