您的位置:首页 > 数据库

MyBatis SQL注入隐患及防范

2017-10-21 00:00 127 查看
摘要: 一次日常项目测试中,MyBatis相关知识的学习及代码可能出现的潜在安全隐患点。

什么是 MyBatis ?

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

常见风险

情况一:

/* mapper设置  */
<select id="getUserbyId" resultType="model.Student">
select * from Studentinfo where id = #{id}
</select>

/* 测试代码 */
Student id = new Student();
id.setId(1);
Student student = session.selectOne("getUserbyId", id);

/* 分析 */
此时setId()的参数只能为Student对应原始的数据类型数据,即只能为正数,不能传入"1"等
mapper处使用#{}、${}方式结果相同

/* 结果 */
无注入风险

情况二:

/* mapper设置  */
<select id="getUserbyId" resultType="model.Student">
select * from Studentinfo where id = #{id}
</select>

/* 测试代码 */
Student student = session.selectOne("getUserbyId", 1);

/* 分析 */
mysql特性,selectOne()传入任何数据,都只会取最前面合法的参数去执行语句,无合法数据将返回null

/* 结果 */
无注入风险

情况三:

/* mapper设置  */
<select id="getUserbyId" resultType="model.Student">
select * from Studentinfo where id = ${id}
</select>

/* 测试代码 */
Student student = session.selectOne("getUserbyId", 1);
selectOne()传入INT,STRING类型数据,都会产生报错提示如下
There is no getter for property named 'id' in 'class java.lang.Integer'

/* 分析 */
${}表示mybatis将传入的对象原样通过get方法获取其值后代入sql语句执行
而如上传入的简单数据类型作为object非对象无get方法,故执行报错

/* 结果 */
无注入风险

情况四:

/* mapper设置  */
<select id="getUserbyId" resultType="model.Student">
select * from Studentinfo where id = ${id}
</select>

/* 测试代码 */
Map params = new HashMap();
params.put("id", 1);
Student student = session.selectOne("getUserbyId", params);

/* 分析 */
selectOne()传入map对象,${}通过get方式获取id属性值原样代入sql语句执行

/* 结果 */
存在注入风险

/* 利用 */
Map params = new HashMap();
params.put("id", "1 or 1=1 limit 0,2");
Student student = session.selectOne("getUserbyId", params);

/* 修复方案 */
#{}方式处理传入的参数

情况五:

/* mapper设置  */
<select id="getUserbyId" resultType="model.Student">
select * from Studentinfo order by ${ordername}
</select>

/* 测试代码 */
Map params = new HashMap();
params.put("ordername", "name");
Student student = session.selectOne("getUserbyId", params);

/* 分析 */

/* 结果 */
存在注入风险

/* 利用 */
Map params = new HashMap();
params.put("ordername", "'");
Student student = session.selectOne("getUserbyId", params);

/* 修复方案 */
#{}方式处理传入的参数
select * from Studentinfo order by #{ordername}
sortname方式相同

情况六:

/* mapper设置  */
<select id="getUserbyId" parameterType="map" resultType="model.Student">
select * from Studentinfo
<if test="ordername != ''">
order by
<if test="ordername == 'name' ">
name
</if>
<if test="ordername == 'id' ">
id
</if>
</if>
</select>

/* 测试代码 */
Map params = new HashMap();
params.put("ordername", "name");
Student student = session.selectOne("getUserbyId", params);

/* 分析 */
传入不存在的odername即可注入

/* 结果 */
存在注入风险

/* 利用 */
Map params = new HashMap();
params.put("ordername", "'");
Student student = session.selectOne("getUserbyId", params);

/* 修复方案 */
方案一:
#{}方式处理传入的参数
select * from Studentinfo order by #{ordername}

方案二:
白名单形式判断传入的ordername是否属于合法
<if test="ordername != '' and ordername in ('name', 'id') ">

情况七:

/* mapper设置  */
<select id="getUserbyId" parameterType="map" resultType="model.Student">
select * from Studentinfo where name like '%${name}%'
</select>

/* 测试代码 */
Map params = new HashMap();
params.put("name", "he");
Student student = session.selectOne("getUserbyId", params);

/* 分析 */

/* 结果 */
存在注入风险

/* 利用 */
Map params = new HashMap();
params.put("name", "'");
Student student = session.selectOne("getUserbyId", params);

/* 修复方案 */
方案一:
使用concat()函数连接
select * from Studentinfo where name like CONCAT('%',#{name},'%')

方案二:
使用官方推荐的bind()函数完成参数绑定
<bind name="pattern" value="'%' + name + '%'" />
select * from Studentinfo where name like #{pattern}

情况八:

/**
* in方式查询类似,可使用foreach方式构建
* 详见官方文档:http://www.mybatis.org/mybatis-3/zh/dynamic-sql.html
*/

参考链接

http://www.mybatis.org/mybatis-3/zh/index.html http://blog.csdn.net/kucoll/article/details/51679371 https://www.google.com.hk https://www.baidu.com/

最后

此文仅为对mybatis的一些初步认识,稍后再遇到相关项目再更新...

20171022更新

京东安全应急响应中心这篇文章不错,更多深入分析见 http://mp.weixin.qq.com/s?__biz=MjM5OTk2MTMxOQ==&mid=2727827368&idx=1&sn=765d0835f0069b5145523c31e8229850&mpshare=1&scene=1&srcid=0926iDLkz6CKTO9IUh5fqt3o#rd

漏洞场景

1. 模糊查询like

还以第一节中提到的新闻详情页面为例,按照新闻标题对新闻进行模糊查询,如果考虑安全编码规范问题,其对应的SQL语句如下:

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

但由于这样写程序会报错,研发人员将SQL查询语句修改如下:

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

在这种情况下我们发现程序不再报错,但是此时产生了SQL语句拼接问题,如果java代码层面没有对用户输入的内容做处理势必会产生SQL注入漏洞。
2. in之后的参数

在对新闻进行同条件多值查询的时候,如当用户输入1001,1002,1003…100N时,如果考虑安全编码规范问题,其对应的SQL语句如下:

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

但由于这样写程序会报错,研发人员将SQL查询语句修改如下:

Select * from news where id in (${id}),

修改SQL语句之后,程序停止报错,但是却引入了SQL语句拼接的问题,如果研发人员没有对用户输入的内容做过滤,势必会产生SQL注入漏洞。
3. order by之后

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

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

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

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

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

修复方案

1. 模糊查询like SQL注入修复建议

按照新闻标题对新闻进行模糊查询,可将SQL查询语句设计如下:

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

采用预编译机制,避免了SQL语句拼接的问题,从根源上防止了SQL注入漏洞的产生。
2.  in之后的参数SQL注入修复建议

在对新闻进行同条件多值查询的时候,可使用Mybatis自带循环指令解决SQL语句动态拼接的问题:

select * from news where id in

<foreach collection="ids" item="item" open="("separator="," close=")">#{item} </foreach>
3. order by SQL注入修复建议--在Java层面做映射

预编译机制只能处理查询参数,其他地方还需要研发人员根据具体情况来解决。如前面提到的排序情景: Select * from news where title =‘京东’ order by #{time} asc,这里time不是查询参数,无法使用预编译机制,只能这样拼接:Select * from news where title =‘京东’ order by ${time} asc 。

针对这种情况研发人员可以在java层面做映射来进行解决。如当存在发布时间time和点击量click两种排序选择时,我们可以限制用户只能输入1和2。当用户输入1时,我们在代码层面将其映射为time,当用户输入2时,将其映射为click。而当用户输入1和2之外的其他内容时,我们可以将其转换为默认排序选择time(或者click)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  mybatis SQL注入 java