Loading ...
Java 常用框架 SQL 注入审计指南
在 Java 开发中,SQL 注入是一种常见且危害巨大的安全漏洞。本文将针对 Java 常用的几种框架,深入分析 SQL 注入的产生原因、常见漏洞场景及安全解决方案,为开发者和安全审计人员提供参考。

一、SQL 注入存在条件

Java 作为强类型语言,SQL 注入风险的存在具有特定条件:仅当String 类型变量参与 SQL 语句构建时,才可能存在注入风险。若参数被强制限定为integer等非字符串类型,当传入非对应类型的值时,会触发类型错误,从而不会产生 SQL 注入。

二、JDBC 框架 SQL 注入分析

(一)危险用法:Statement

  • 原理:通过字符串拼接的方式构造 SQL 语句,没有预编译机制。当用户可控的输入未经过滤直接拼接到 SQL 语句中时,攻击者可以篡改 SQL 逻辑,实现注入攻击。
  • 示例代码
// Web接口场景
@RequestMapping("/jdbc/vuln")
public String jdbc_sqli_vul(@RequestParam("username") String username) {
    String sql = "select * from users where username = '" + username + "'";
    Statement statement = con.createStatement();
    ResultSet rs = statement.executeQuery(sql);
    return "查询结果";
}
  • 漏洞验证:当输入joychou' or '1'='1时,生成的 SQL 语句为select * from users where username = 'joychou' or '1'='1',该语句会返回全部用户数据,造成信息泄露。

(二)安全用法:PreparedStatement

  • 原理:采用预编译机制,使用?作为占位符,通过setXxx()方法绑定参数。这种方式会将用户输入作为数据处理,而不是 SQL 语句的一部分,因此无法改变 SQL 结构,能有效防止注入。
  • 示例代码
String sql = "select * from users where username = ?";
PreparedStatement st = con.prepareStatement(sql);
st.setString(1, username);
ResultSet rs = st.executeQuery();
  • 优势:不仅能防止 SQL 注入,还能减少 SQL 语句的重复编译,提升系统性能。

(三)高频漏洞场景

漏洞场景 危险代码示例 安全解决方案
未用占位符 sql = "select * from users where id=?" + " and username like '%" + username1 + "%'"; 全量使用占位符,如sql = "select * from users where id=? and username like ?";,然后绑定参数
in语句拼接 String sql = "delete from users where id in(" + delIds + ")"; 遍历生成占位符或使用foreach循环绑定参数
like语句拼接 String sql = "select * from users where password like '%" + con + "%'"; 使用数据库函数拼接,如 MySQL 的like concat('%', #{param}, '%')
order by无预编译 String sql = "select * from news where title =?" + "order by '" + time + "' asc"; 用字段列数预编译或严格过滤字段名白名单
未过滤通配符 用户输入含%导致like '%a%'全匹配 手动过滤%_通配符

三、MyBatis 框架 SQL 注入分析

(一)核心风险点:#{} 与 ${}

特性 #{ }(安全) ${ }(危险)
处理方式 转换为预编译?,进行参数绑定 直接进行字符串拼接,没有预编译过程
防注入能力 支持 不支持
适用场景 普通参数(如where id=#{id} 必须进行拼接的场景(如order by ${column},此时需要额外过滤)

(二)高频漏洞场景

1. 模糊查询

  • 危险用法:由于like '%#{param}%'存在语法错误,很多开发者会改用${ }进行拼接,从而引入风险:
<select id="findByUserNameVuln" resultType="User">
    select * from users where username like '%${_parameter}%'
</select>
  • 安全用法:使用数据库函数实现预编译,避免直接拼接:
<select id="findByUserNameSec" resultType="User">
    select * from users where username like concat('%', #{_parameter}, '%')
</select>

2. in多值查询

  • 危险用法:直接用${ }拼接in语句,会导致注入风险:
<select id="findByUserNameVuln04" resultType="User">
    select * from users where id in (${id})
</select>
  • 安全用法:使用foreach循环生成占位符,实现参数绑定:
<select id="selectBookByIds" parameterType="list" resultType="Book">
    SELECT * FROM Book WHERE id IN
    <foreach collection="list" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

3. order by排序

  • 危险用法:因为#{ }会给字段加引号,导致排序失效,所以错误地使用${ }进行拼接:
<select id="getUsersBySort" resultType="User">
    SELECT * FROM users ORDER BY ${sortColumn}
</select>
  • 安全用法:可以采用字段列数预编译的方式,或者严格过滤字段名,只允许白名单中的字段参与排序。

(三)实战审计案例

以某医院管理系统(Spring Boot + MyBatis)为例:
  1. 审计入口:在 Mapper 文件中搜索${关键字,定位可能存在风险的代码位置。
  2. 漏洞代码:发现存在SELECT distinct ${column} FROM users这样的代码,其中column由路由参数传入,攻击者可以控制该参数。
  3. 验证 Payload:通过访问http://xxx/option/users/(select%20database()),成功获取到数据库名,证实了 SQL 注入漏洞的存在。

四、MyBatis-Plus 框架 SQL 注入分析

(一)安全机制

QueryWrapper/UpdateWrapper的基础方法(如eqlikein等)会自动进行预编译处理,有效防止 SQL 注入:
@RequestMapping("/like")
public List<Tutorial> mybatislike(String author) {
    QueryWrapper<Tutorial> wrapper = new QueryWrapper<>();
    wrapper.like("author", author); 
    return tutorialMapper.selectList(wrapper);
}

(二)高频漏洞场景

危险方法 漏洞代码示例 安全解决方案
apply()直接拼接 wrapper.apply("title=" + title); 使用{index}占位符绑定参数
last()拼接尾部 wrapper.last("order by " + column); 过滤排序字段白名单
exists()/notExists() wrapper.exists("select title from t where title = " + title); 使用占位符绑定参数
having()拼接条件 wrapper.having("id > " + id); 使用{index}占位符
orderBy()/groupBy() wrapper.orderByAsc(column); 过滤字段名白名单
分页插件addOrder() personPage.addOrder(OrderItem.asc(order)); 过滤order参数

(三)自定义 SQL 风险

在使用#{ew.customSqlSegment}时,如果Wrapper中包含未进行预编译的方法(如lastorderBy等),可能会引发 SQL 注入漏洞。

五、Hibernate/JPA 框架 SQL 注入分析

(一)危险用法

  • HQL 注入示例
String userInput = "2 or 1=1";
String hql = "from Book where id = " + userInput;
Query<Book> query = session.createQuery(hql, Book.class);
Book book = query.uniqueResult();
  • 原生 SQL 注入示例
String sql = "select * from user where username = '" + username + "'";
Query<People> query = session.createNativeQuery(sql);

(二)安全用法

安全方式 示例代码 适用场景
命名参数绑定 String hql = "from users where name = :name"; query.setParameter("name", parameter); 复杂查询
位置参数绑定 String hql = "from users where name = ?1"; query.setParameter(1, parameter); 简单查询
命名参数列表 query.setParameter("names", Arrays.asList("g1ts", "g2ts")); in多值查询
Criteria API CriteriaQuery<Book> cq = cb.createQuery(Book.class); cq.where(cb.equal(root.get("id"), bookId)); 动态多条件查询
session.get() Book book = session.get(Book.class, bookId); 主键查询

(三)HQL 注入限制

HQL 注入相比原生 SQL 注入,存在一些限制:
  • 不支持UNION SELECT语句;
  • 无法访问系统表;
  • 需要知晓实体类的字段名才能进行有效的注入;
  • 不支持--注释,部分情况下支持/* comment */注释。
通过了解以上各框架中 SQL 注入的风险点和安全用法,开发者可以在实际开发中采取相应的防范措施,有效降低 SQL 注入漏洞的发生概率,提高系统的安全性。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇