Mybatis是作为一款半自动化的持久层框架,因为sql语句都要我们自己手动编写还要注意安全问题,所以Mybatis在一定程度上帮我们避免了这些问题,但是Mybatis不意味着一定的安全,它总共有两个参数# {id}和 ${id}。
- #{id}:相当于JDBC中的PreparedStatement,预编译处理参数,可以有效的避免注入
- ${id}:是未经过预编译,仅仅取输出变量的值
总的来说,使用#{id}是通过预编译处理后 ,不会存在注入的情况,而${id}则可能会出现,在实际业务中,有些情况还必须使用出这种拼接的方式。主要出现在以下几种情况:
- 使用like 关键字
- 使用order by关键字
- 使用in关键字
一、idea环境配置:
为了方便测试语句的顺利执行,暂时对sql语句的报错进行忽略,进行强制编译。idea设置如下:
1.setting->java compiler
2.run->Edit configuration->选择编译,不进行错误校验。
二、数据库、PO、映射文件、配置文件等。
其中数据库的user表为下图所示:
三、使用like 关键字
3.1关键代码:
User的xml配置如下:
<select id="findUserByName" parameterType="String" resultType="it.lingwu.sec.PO.User">
SELECT * FROM USER WHERE USERNAME LIKE '%${value}%'
</select>
测试用例如下:
public class TestLike {
@Test
public void test() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
SqlSession session = build.openSession();
//User user = session.selectOne("findUserById", 1);
List<User> user=session.selectList("findUserByName","陈");
System.out.println(user);
session.close();
}
}
可以看到,此时正确返回2个结果,返回结果如下:
[User{id=22, username='陈大明', birthday=2015-02-09, sex='1', address='天津市'},
User{id=25, username='陈小明', birthday=2015-02-01, sex='1', address='深圳市'}]
修改传入的值为”陈%’ or username like ‘%’ – “”,返回User表中所有数据,
List<User> user=session.selectList("findUserByName","陈%' or username like '%' -- ");
3.2修复方式
可将‘%${value}%’ ,替换为#{id}的形式,并用concat函数将%与其拼起来, 如下代码所示:
<select id ="findUserByNameTrue" parameterType="String" resultType="it.lingwu.sec.PO.User">
SELECT * FROM USER WHERE USERNAME LIKE CONCAT('%',#{id},'%')
</select>
能过这种拼接的形式,仍然采用#{}占位的方法,有效避免sql注入的风险。
四、使用order by关键字
4.1关键代码:
User的xml配置如下:
<select id="findUserById" parameterType="String" resultType="it.lingwu.sec.PO.User">
select * from user ORDER BY ${value} DESC
</select>
测试用例:
public class TestOrderBy {
@Test
public void test() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
SqlSession session = build.openSession();
List<User> user=session.selectList("findUserById","IF(1=1,id,sex) ");
System.out.println(user);
session.close();
}
}
执行IF(1=1,id,sex)语句后,输出结果正常,可以看到,以id的逆序排序:
[User{id=25, username='陈小明', birthday=2015-02-01, sex='1', address='深圳市'}, User{id=24, username='张三丰', birthday=2015-01-09, sex='1', address='天津市'}, User{id=22, username='陈大明', birthday=2015-02-09, sex='1', address='天津市'}, User{id=16, username='张小明', birthday=2015-07-09, sex='1', address='上海市'}, User{id=10, username='张三', birthday=2014-07-10, sex='1', address='北京市'}, User{id=1, username='王五', birthday=null, sex='2', address='null'}]
执行IF(1=12,id,sex)语句后,可以看到,以性别的逆序排序:
[User{id=1, username='王五', birthday=null, sex='2', address='null'}, User{id=10, username='张三', birthday=2014-07-10, sex='1', address='北京市'}, User{id=16, username='张小明', birthday=2015-07-09, sex='1', address='上海市'}, User{id=22, username='陈大明', birthday=2015-02-09, sex='1', address='天津市'}, User{id=24, username='张三丰', birthday=2015-01-09, sex='1', address='天津市'}, User{id=25, username='陈小明', birthday=2015-02-01, sex='1', address='深圳市'}]
4.2修复方式
通过映射的方式解决该问题。如,id映射为1,sex映射为2,只允许用户输1与2。当输入1和2之外的其他内容时,将其转换为默认排序选择1。
五、使用in关键字
5.1关键代码:
User的xml配置如下:
<select id="findUserByIn" parameterType="String" resultType="it.lingwu.sec.PO.User">
SELECT * FROM USER where id in (${value})
</select>
测试用例:
@Test
public void test() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
SqlSession session = build.openSession();
List<User> user=session.selectList("findUserByIn","1,24");
System.out.println(user);
session.close();
}
执行后,正常返回两个结果:
[User{id=1, username='王五', birthday=null, sex='2', address='null'}, User{id=24, username='张三丰', birthday=2015-01-09, sex='1', address='天津市'}]
将执行语句改成“select id from user”后,返回6个结果:
[User{id=1, username='王五', birthday=null, sex='2', address='null'}, User{id=10, username='张三', birthday=2014-07-10, sex='1', address='北京市'}, User{id=16, username='张小明', birthday=2015-07-09, sex='1', address='上海市'}, User{id=22, username='陈大明', birthday=2015-02-09, sex='1', address='天津市'}, User{id=24, username='张三丰', birthday=2015-01-09, sex='1', address='天津市'}, User{id=25, username='陈小明', birthday=2015-02-01, sex='1', address='深圳市'}]
5.2修复方式
UserMapeper.xml文件如下:
<select id="findUserByInTrue" parameterType="String" resultType="it.lingwu.sec.PO.User">
SELECT * FROM USER
<where>
<if test="ids!=null and ids.size>0">
<foreach collection="ids" item="id" open="id in (" close=")" separator="," >
#{id}
</foreach>
</if>
</where>
</select>
测试用例:
public class TestIn {
@Test
public void test() throws IOException {
InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
SqlSession session = build.openSession();
UserMap userMapper=session.getMapper(UserMap.class);
User query = new User();
ArrayList<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(10);
query.setIds(ids);
List<User> users = userMapper.findUserByInTrue(query);
System.out.println(users);
session.close();
}
}
六、总结
从白盒角度来看,可直接从相关文件搜索“$”,再进一步确认;
从黑盒角度来看,可重点关注可能有like,order by,in等关键词的sql语句的地方。