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注入问题
- MyBatis如何防止SQL注入
- mybatis防止sql注入 原理
- mybatis是如何防止SQL注入的
- [Daily]跨站脚本、SQL注入、密码管理防范与修复方法
- java持久层框架mybatis如何防止sql注入
- SQL注入的一般步骤及防范方法
- SQL注入及防范措施【转】
- 信息安全的隐患-GoogleHacking原理和防范
- MyBatis如何防止SQL注入
- SQL注入的一般步骤及防范方法
- Mybatis如何防止sql注入?
- Mybatis sql注入问题
- SQL注入种类及防范
- 终极防范SQL注入漏洞
- Ajax时代 SQL注入依然是隐患
- php web开发安全之sql注入和防范:(一)简单的select语句注入和防范
- 邮件安全隐患及其防范技术研究
- php防止SQL注入详解及防范
- 三个步骤 堵死网站被SQL注入的隐患