java代码审计自学:sql注入篇
0x00 前提
主要是因为自己的学习Java 代码审计中的学习思路吧,主要自己一个人学习,有点闭门造车,百度学习法,但是还是记录一下,也分享一下,也便于将来的总结和反思,如果我能终能学到什么,我也会重新梳理思路,为那些自学者提供一个好的思路,所以有了下面的系列文章java代码审计自学篇。
0x01 漏洞原理
虽然基础,但是还是介绍一下吧
SQL 注入老生常谈,就是 SQL 命令插入请求中,并在服务器端被接收后用,没有有效的过滤,导致服务器执行了意料之外的恶意的 SQL 命令,最终达到恶意的脱数据。
Java 的 SQL 注入和 PHP 中的 SQL 注入,其实原理都是一样的,理论上只要是与数据库存在数据交互,只要传入的数据完全受用户控制,没有有效的过滤都有可能出现 SQL 注入的。
java的特殊是有一些框架会托管一部分的数据库的操作,我们要了解一下
0x02 分类:拼接和预编译
1、直接拼接,未进行过滤
将request.getParameter("id")
获取的id直接放在SQL语句,没有过滤而且是拼接的情况
以前的JDBC的方式,的直接拼接方式,存在sql注入:
1 | request.getParameter("userId") |
全局搜索查看:="
或者sql
的问题
防止措施,修复方法
1 | //安全的,预编译的,防止了sql注入 |
2、Mybatis和Hibernate 框架
主要是预编译的错误使用
框架主要都是使用注解或者xml将java对象与数据库sql操作对应。
下面以 Mybatis 讲解一下
- mybatis的maven配置
1 | <dependency> |
- config.xml 配置数据库连接的文件
1 | <?xml version="1.0" encoding="UTF-8" ?> |
主要注意的点!
- UserMapper.xml
正常预编译
1 | <select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap"> |
存在漏洞的预编译
1 | <select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap"> |
#和$的区别:
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、模糊查询
1 | Select * from news where title like ‘%#{title}%’ |
在这种情况下使用#程序会报错,新手程序员就把#号改成了$,这样如果java代码层面没有对用户输入的内容做处理势必会产生SQL注入漏洞。
正确写法:
1 | select * from news where tile like concat(‘%’,#{title}, ‘%’) |
2、in 之后的多个参数
in之后多个id查询时使用# 同样会报错
1 | Select * from news where id in (#{ids}) |
正确用法为使用foreach,而不是将#替换为$
1 | id in<foreach collection="ids" item="item" open="("separatosr="," close=")">#{ids} </foreach> |
3、order by 之后
默认情况下,使用#{}格式的语法会导致MyBatis的创建的PreparedStatement参数并安全地设置参数(就像使用?一样)。这样做更安全,更迅速,通常也是首选做法。
当根据发布时间、点击量等信息对新闻进行排序的时候,如果考虑安全编码规范问题,其对应的SQL语句如下:
1 | Select * from news where title =‘123’ order by #{time} asc |
但由于发布时间time不是用户输入的参数,无法使用预编译。研发人员将SQL查询语句修改如下:
1 | 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语句中定义命名参数要用”:”开头,形式如下:
1 | Query query=session.createQuery(“from User user where user.name=:customername and user:customerage=:age ”); |
按参数位置邦定:
在HQL查询语句中用”?”来定义参数位置,形式如下:
1 | Query query=session.createQuery(“from User user where user.name=? and user.age =? ”); |
setParameter()方法: ,
在Hibernate的HQL查询中可以通过setParameter()方法邦定任意类型的参数,如下代码:
1 | String hql=”from User user where user.name=:customername ”; |
setProperties()方法:
1 | Entity entity=new Entity(); |
0x05 挖掘方法
使用idea 搜索$关键字
可以先筛选xml文件搜索$,逐个分析,要特别注意mybatis-generator的order by注入
Ctrl+shift+F 调出Find in Path,筛选后缀xml,搜索$关键字
找到是mybatis的数据库文件
找到调用函数后,alt+f7查看调用链,检查中间是否被过滤