java代码审计自学:sql注入篇

0x00 前提

主要是因为自己的学习Java 代码审计中的学习思路吧,主要自己一个人学习,有点闭门造车,百度学习法,但是还是记录一下,也分享一下,也便于将来的总结和反思,如果我能终能学到什么,我也会重新梳理思路,为那些自学者提供一个好的思路,所以有了下面的系列文章java代码审计自学篇。

0x01 漏洞原理

虽然基础,但是还是介绍一下吧

SQL 注入老生常谈,就是 SQL 命令插入请求中,并在服务器端被接收后用,没有有效的过滤,导致服务器执行了意料之外的恶意的 SQL 命令,最终达到恶意的脱数据。

Java 的 SQL 注入和 PHP 中的 SQL 注入,其实原理都是一样的,理论上只要是与数据库存在数据交互,只要传入的数据完全受用户控制,没有有效的过滤都有可能出现 SQL 注入的。

java的特殊是有一些框架会托管一部分的数据库的操作,我们要了解一下

0x02 分类:拼接和预编译

1、直接拼接,未进行过滤

request.getParameter("id")获取的id直接放在SQL语句,没有过滤而且是拼接的情况

以前的JDBC的方式,的直接拼接方式,存在sql注入:

request.getParameter("userId")

private String getNameByUserId(String userId) {
    Connection conn = getConn();//获得连接
    String sql = "select name from user where id=" + userId;
    PreparedStatement pstmt =  conn.prepareStatement(sql);
    ResultSet rs=pstmt.executeUpdate();
}

全局搜索查看:="或者sql的问题

防止措施,修复方法

//安全的,预编译的,防止了sql注入
Connection conn = getConn();//获得连接
String sql = "select id, username, password, role from user where id=?"; //执行sql前会预编译号该条语句
PreparedStatement pstmt = conn.prepareStatement(sql); 
pstmt.setString(1, id); 
ResultSet rs=pstmt.executeUpdate(); 

2、Mybatis和Hibernate 框架

主要是预编译的错误使用

框架主要都是使用注解或者xml将java对象与数据库sql操作对应。

下面以 Mybatis 讲解一下

  • mybatis的maven配置
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>
  • config.xml 配置数据库连接的文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://localhost:3306/mybatistest"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>

主要注意的点!

  • UserMapper.xml

正常预编译

<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = #{username,jdbcType=VARCHAR}
and password = #{password,jdbcType=VARCHAR}
</select>

存在漏洞的预编译

<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">
select id, username, password, role
from user
where username = ${username,jdbcType=VARCHAR}
and password = ${password,jdbcType=VARCHAR}
</select>

#和$的区别:

1、#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号,正确的预编译,输入的参数会全部变成查询的部分

2、 将 传 入 的 数 据 直 接 拼 接 在 s q l 中 。 造 成 s q l 注 入 如 : w h e r e u s e r n a m e = 将传入的数据直接拼接在sql中。造成sql注入 如:where username= sqlsqlwhereusername={username},如果传入的值是111,那么解析成sql时的值为where username=111;
如果传入的值是1 and 1=1 ;,则解析成的sql为:select id, username, password, role from user where username=1 and 1=1

0x03 MyBatis框架易产生SQL注入漏洞的三种情况:

1、模糊查询

Select * from news where title like ‘%#{title}%’

在这种情况下使用#程序会报错,新手程序员就把#号改成了$,这样如果java代码层面没有对用户输入的内容做处理势必会产生SQL注入漏洞。

正确写法:

select * from news where tile like concat(‘%’,#{title}, ‘%’)

2、in 之后的多个参数

in之后多个id查询时使用# 同样会报错

Select * from news where id in (#{ids})

正确用法为使用foreach,而不是将#替换为$

id in<foreach collection="ids" item="item" open="("separatosr="," close=")">#{ids} </foreach>

3、order by 之后

默认情况下,使用#{}格式的语法会导致MyBatis的创建的PreparedStatement参数并安全地设置参数(就像使用?一样)。这样做更安全,更迅速,通常也是首选做法。

当根据发布时间、点击量等信息对新闻进行排序的时候,如果考虑安全编码规范问题,其对应的SQL语句如下:

Select * from news where title =‘123’ order by #{time} asc

但由于发布时间time不是用户输入的参数,无法使用预编译。研发人员将SQL查询语句修改如下:

Select * from news where title =‘123’ order by ${time} asc

修改之后,程序通过预编译,但是产生了SQL语句拼接问题,极有可能引发SQL注入漏洞。

不过有时你就是想直接在SQL语句中插入一个不转义的字符串。比如,像ORDER BY,你可以这样来使用:ORDER BY $ {COLUMNNAME}。这里的MyBatis不会修改或者转义字符串。

需要注意的是在mybatis-generator自动生成的SQL语句中,order by使用的也是$,而like和in没有问题。

0x04 Hibernate防止SQL注入

对参数名称进行绑定在HQL语句中定义命名参数要用”:”开头,形式如下:

Query query=session.createQuery(“from User user where user.name=:customername and user:customerage=:age ”); 
query.setString(“customername”,name); 
query.setInteger(“customerage”,age); 

按参数位置邦定:
在HQL查询语句中用”?”来定义参数位置,形式如下:

Query query=session.createQuery(“from User user where user.name=? and user.age =? ”); 
query.setString(0,name); 
query.setInteger(1,age); 

setParameter()方法: ,
在Hibernate的HQL查询中可以通过setParameter()方法邦定任意类型的参数,如下代码:

String hql=”from User user where user.name=:customername ”; 
Query query=session.createQuery(hql); 
query.setParameter(“customername”,name,Hibernate.STRING); 

setProperties()方法:

Entity entity=new Entity();
entity.setXx(“xx”);
entity.setYy(100);
Query query=session.createQuery(“from Entity c where c.xx=:xx and c.yy=:yy ”); 
query.setProperties(entity);

0x05 挖掘方法

使用idea 搜索$关键字

可以先筛选xml文件搜索$,逐个分析,要特别注意mybatis-generator的order by注入

Ctrl+shift+F 调出Find in Path,筛选后缀xml,搜索$关键字

找到是mybatis的数据库文件

找到调用函数后,alt+f7查看调用链,检查中间是否被过滤

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值