MyBatis与SQL注入

MyBatis中#{}与${}的区别

  在mybatis中#{}可以防止SQL注入而${}不可以。举个例子:http://www.xx.com/news.jsp?id=1 。这里的sql语句中如果使用${},

1
SELECT title,content FROM news WHERE id = ${id};

执行时,参数id会被直接拼接入sql语句:

1
SELECT title,content FROM news WHERE id = 1

如果攻击者提交的参数为“id=1 and 1=2 UNION SELECT username, password FROM admin”。拼接的sql语句就变为了:

1
SELECT title,content FROM news WHERE id = 1 and 1=2 UNION SELECT username, password FROM admin

  这条sql的原意就会被改变,导致将管理员数据表中的用户显示在页面title位置,密码显示在页面content位置,达到成功攻击的效果。而如果用#{},

1
SELECT title,content FROM news WHERE id = #{id}

在用户提交参数之前,sql语句会进行一次预编译,

1
SELECT title,content FROM news WHERE id = ?

  攻击者提交的参数中包含的sql编译字符,不会被带入sql进行编译,只作为参数,不能造成sql注入。而且由于只进行一次预编译,sql的性能也会得到提升。在项目中,大部分SQL语句对参数的处理方式都是用了#{}这种预编译方式。但在模糊查询中,使用#{}会报错。所以使用了${ }方式代替#{},其实在项目中可以使用concat的方式,进行参数的拼接。

MyBatis框架下易产生SQL注入漏洞场景分析

  MyBatis框架下易产生SQL注入漏洞的情况主要分为以下三种:

模糊查询like

  以按照标题进行模糊查询为例,如果考虑安全编码规范问题,其对应的SQL语句如下:

1
select * from news where title like '%#{title}%'

  但由于这样写程序会报错,所以将SQL查询语句修改如下:

1
select * from news where title like '%${title}%'

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

in之后的参数

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

1
select * from news where id in (#{id})

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

1
select * from news where id in (${id})

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

order by之后

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

1
select * from news where title =‘test’ order by #{time} asc

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

1
select * from news where title =‘test’ order by ${time} asc

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

Mybatis框架下SQL注入漏洞修复建议

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

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

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

采用预编译机制,避免了SQL语句拼接的问题,从根源上防止了SQL注入漏洞的产生。

in之后的参数SQL注入修复建议

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

1
2
3
4
select * from news where id in
<foreach collection='ids' item='item' open='(' separator=',' close=')'>
#{item}
</foreach>
order by SQL注入修复建议–在Java层面做映射

  预编译机制只能处理查询参数,其他地方还需要研发人员根据具体情况来解决。如前面提到的排序情景:
    

1
select * from news where title =‘test’ order by #{time} asc

  这里无法使用预编译机制,只能这样拼接:
   

1
select * from news where title =‘title’ order by ${time} asc

  针对这种情况可以在java层面做映射来进行解决。如当存在发布时间time和点击量click两种排序选择时,可以限制用户只能输入1和2。当用户输入1时,在代码层面将其映射为time,当用户输入2时,将其映射为click。而当用户输入1和2之外的其他内容时,可以将其转换为默认排序选择time(或者click)。


参考

  • 公司内部分享