Java MyBatis3(7)参数取值以及防止SQL 注入

序言

在mybatis中,参数取值方式有两种:#{ } 和 ${ }

一、#{ }

select * from student where name=#{name}

编译后执行的sql语句:

select * from student where name=?

说明:

 #{ }实现的是JDBC 中preparedStatement中的占位符。

#{ }适合sql语句中的参数传值,构建sql语句#{ }是不可以的。

select * from #{tablename} ;

编译后的sql语句为:

select * from ?

这在sql中是不允许的,所以要用${ 拼接}

#{ }试用的场景

1.where语句里的判断:

a=#{a},a>#{a},a in {#{a}},a like #{a}........

2.set语句:

set a=#{a}

3.插入语句中:

values(#{a},.......)

4.其他大部分适合${ }进行拼接

二、${ }

select * from student where name=${name}

编译后执行的sql语句:

select * from student where name=name

说明:

${ }取参方式是简单的字符串拼接,不适合进行参数传值,不然会有sql语句注入的危险。

它更加适合的是构建sql语句。

Mybatis 框架下的 SQL 注入问题及防护方法

1、模糊查询

在模糊查询场景下,考虑安全编码规范,使用 #{} 传入参数:

<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">
    select * from user_table where username like '%#{username}%'
</select>

在这种情况下使用 #{} 程序会报错:

于是很多安全经验不足的程序员就把 #{} 号改成了 ${},如果应用层代码没有对用户输入的内容做处理势必会产生SQL注入漏洞。

<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">
    select * from user_table where username like '%${username}%'
</select>

因此,安全的写法应当使用 CONCAT 函数连接通配符

<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">
    select * from user_table where username like concat('%',#{username},'%')
</select>

2、带有 IN 谓词的查询

在 IN 关键字之后使用 #{} 查询多个参数:

 <select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">
    select * from user_table where username in (#{usernames})
</select>

正常提交查询参数 'zxd','hhh',因为预编译机制,系统将我们输入的字符当作了一个字符串,因此查询结果为空,不能满足业务功能需求。

于是很多安全经验不足的程序员就把 #{} 号改成了 ${} :

<select id="getUser" parameterType="java.lang.String" resultType="user.NewUserDO">
    select * from user_table where username in (${usernames})
</select>

因此,此种情况下,安全的做法应当使用 foreach 标签

<select id="getUserFromList" resultType="user.NewUserDO">
    select * from user_table where username in
        <foreach collection="list" item="username" open="(" separator="," close=")">
            #{username}
        </foreach>
</select>

3、带有动态排序功能的查询

动态排序功能,需要在 ORDER BY 之后传入参数,考虑安全编码规范,使用 #{} 传入参数:

<select id="getUserOrder" parameterType="java.lang.String" resultType="user.NewUserDO">
    select * from user_table order by #{column} limit 0,1
</select>

提交参数 username 根据用户名字段排序。但因为预编译机制,系统将我们输入的字符当作了一个字符串,根据字符串排序是不生效的,不能满足业务功能需求。(根据用户名字段排序,此时正常应返回 root 用户)

于是很多安全经验不足的程序员就把 #{} 号改成了 ${} :

<select id="getUserOrder" parameterType="java.lang.String" resultType="user.NewUserDO">
    select * from user_table order by ${column} limit 0,1
</select>

攻击者提交参数值 username#,利用 SQL 注入漏洞,成功查询了所有用户数据。

因此,此种情况下,安全的做法应当在 Java 代码层面来进行解决。可以设置一个字段值的白名单,仅允许用户传入白名单内的字段。

String sort = request.getParameter("sort");
String[] sortWhiteList = {"id", "username", "password"};
if(!Arrays.asList(sortWhiteList).contains(sort)){
    sort = "id";
} 

或者仅允许用户传入索引值,代码再将索引值映射成对应字段。

String sort = request.getParameter("sort");
switch(sort){
    case "1":
        sort = "id";
        break;
    case "2":
        sort = "username";
        break;
    case "3":
        sort = "password";
        break;
    default:
        sort = "id";
        break;
} 

需要注意的是在 mybatis-generator 自动生成的 SQL 语句中,ORDER BY 使用的也是 ${},而 LIKE 和 IN 没有问题。

资料

Mybatis下的SQL注入漏洞原理及防护方法

posted @ 2018-07-21 13:03  ~沐风  阅读(337)  评论(0编辑  收藏  举报