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 没有问题。

浙公网安备 33010602011771号