🔥 Java源码SQL注入深入解析
📖 一、引言
SQL 注入漏洞长期占据 OWASP Top 10,其危害性与利用难度使其成为红队渗透中最常见的突破口之一。Java 作为主流后端语言,其常见的数据访问实现方式 JDBC、MyBatis 和 Hibernate 中的代码若处理不当,极易成为攻击者利用的入口。
💻 二、源码中的SQL执行方式与漏洞成因
🔎 2.1 JDBC方式执行SQL
✅ 安全与❌风险对比:Statement 与 PreparedStatement
❌ A. 使用 Statement 直接拼接SQL
String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";Statement stmt = connection.createStatement();ResultSet rs = stmt.executeQuery(sql);
🔍 问题分析:
-
用户输入
username
和password
被直接拼接到 SQL 中。 -
如果攻击者输入:
' OR '1'='1
,SQL 变为:SELECT * FROMusersWHERE username = ''OR'1'='1'ANDpassword = ''
-
无条件成立,造成认证绕过或数据泄露。
✅ B. 使用 PreparedStatement 参数化防注入
String sql = "SELECT * FROM users WHERE username = ? AND password = ?";PreparedStatement pstmt = connection.prepareStatement(sql);pstmt.setString(1, username);pstmt.setString(2, password);ResultSet rs = pstmt.executeQuery();
🔐 安全性分析:
-
使用 ?
占位符防止拼接。 -
JDBC 自动将参数转义为字符串字面量。 -
无法插入 OR
、; DROP TABLE
等控制符。
❌ C. 部分参数仍被拼接
String sql = "SELECT * FROM users WHERE username = ? AND password = '" + password + "'";PreparedStatement pstmt = connection.prepareStatement(sql);pstmt.setString(1, username);
⚠️ 误区分析:
-
password
仍然通过拼接传入,攻击者可借此注入。 -
误用 PreparedStatement 并不安全,必须全参数化。
🔁 D. 排序/IN等动态SQL无法预编译的场景
String columnName = request.getParameter("sort");String sql = "SELECT * FROM users ORDER BY " + columnName;
🧨 注入点解释:
-
用户控制排序字段,可能注入如 username desc; DROP TABLE users; --
。 -
在排序字段上攻击可导致联合查询、删除表等。
✅ 防护策略:
if (!Arrays.asList("username", "id", "created_at").contains(columnName)) {thrownew IllegalArgumentException("非法排序字段");} //采用白名单校验String sql = "SELECT * FROM users ORDER BY " + columnName;
🧩 2.2 MyBatis方式执行SQL
❌ A. 使用 ${}
导致拼接注入
@Select("SELECT * FROM users WHERE username = '${username}'")
🔍 问题分析:
-
${}
会直接字符串替换,不做转义。 -
传入 admin' OR '1'='1
可绕过认证。
✅ 建议使用:
@Select("SELECT * FROM users WHERE username = #{username}")
-
#{}
会自动进行参数绑定与类型转义,防止SQL注入。
⚙️ B. MyBatis XML中的动态字段处理
<selectid="listUser"resultType="User"> SELECT * FROM users<where><iftest="username != null"> AND username = #{username}</if></where> ORDER BY <choose><whentest="orderBy == 'username'">username</when><otherwise>id</otherwise></choose></select>
🔐 解读:
-
<choose>
结构限制ORDER BY
字段来源,避免动态拼接字段注入。 -
<if>
标签条件判断防止空值拼接后SQL错误或注入发生。
Mybatis框架下SQL注入详细分析见这篇《一文吃透 MyBatis 执行 SQL 的全过程,附源码解析!》
🧵 2.3 Hibernate方式执行SQL
❌ A. 拼接HQL导致注入
String hql = "FROM User u WHERE u.username = '" + username + "'";Query query = session.createQuery(hql);
🚨 风险描述:
-
拼接HQL类似拼接SQL,攻击者可植入 ' or '1'='1
造成HQL注入。
✅ B. 使用命名参数
String hql = "FROM User u WHERE u.username = :username";Query query = session.createQuery(hql);query.setParameter("username", username);
🔐 安全性说明:
-
Hibernate 自动将参数转义,防止注入。 -
建议使用 createNativeQuery
时同样绑定参数。
🚩 三、红队视角下的攻击流程
🎯 1. 信息收集
-
查看源码、反编译Jar、抓取数据包和接口参数; -
聚焦拼接SQL、动态字段构建点。
🧪 2. 构造Payload
-
经典认证绕过: ' OR '1'='1' --
-
联合注入/布尔盲注: ' UNION SELECT null, user(), version() --
🔄 3. 规避过滤与绕过
-
使用编码、注释绕过(如: %27or/**/1=1--
) -
构造深度payload对抗 WAF。
🧰 4. 提权与横向移动
-
利用注入漏洞提取数据库结构、用户凭据; -
登录后台系统,进行RCE(如文件上传)与横向攻击。
🧱 四、防御策略与安全编码建议
为防止源码中出现的SQL注入漏洞,可以在安全报告中提出如下防护建议:
-
🛡 全面采用预编译查询 -
在JDBC中,务必使用PreparedStatement对所有参数化数据进行预编译。 -
在MyBatis中,使用 #{}
替代${}
,确保参数自动转义。 -
📋 动态字段使用白名单严格控制 -
对于如排序、IN查询等特殊场景,构建更为严格的白名单机制,多重验证输入合法性。 -
🔎 SQL关键字过滤与WAF结合 -
通过Servlet全局过滤器对传入参数进行安全校验,拦截明显的SQL注入攻击。 -
配置细粒度日志记录与异常处理,避免敏感信息泄露。 -
🔐 后台异常信息不回显 -
统一报错内容,避免详细报错信息输出 -
🧪 定期渗透测试与源码审计 -
📘 安全编码规范培训与代码上线审批 -
定期组织安全培训,提高开发人员对SQL注入及相关风险的认知。 -
制定严格的编码规范和审计流程,降低因疏忽导致安全问题的概率。
📌 五、总结
Java源码中的SQL注入漏洞往往因开发不严谨而引发严重后果,从攻击视角来看,只要存在任何未经过滤或未充分参数化的输入处理,就可能成为黑客攻破数据库的突破口。通过本文对各类SQL执行方式及漏洞成因的深入分析,我们可以看到:
-
防御绝不是一个环节的事,而是需要全链路安全保障。 -
无论技术栈如何变迁,安全的编码习惯与严格的审计流程始终是企业信息安全的坚实基石。
原文始发于微信公众号(季升安全):开发没注意,这些Java代码让黑客轻松拿下数据库!
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论