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=
将传入的数据直接拼接在sql中。造成sql注入如:whereusername={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查看调用链,检查中间是否被过滤