MyBatis中#{}与${}的区别 sql注入

343 阅读3分钟

这是我参与11月更文挑战的第28天,活动详情查看:2021最后一次更文挑战

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

在mybatis中#{}可以防止SQL注入而${}不可以。举个例子:

http://www.xx.com/news.jsp?id=1 

这里的sql语句中如果使用${},

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

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

  SELECT title,content FROM news WHERE id = 1;

如果攻击者提交的参数为

  “id=1 and 1=2 UNION SELECT username, password FROM admin”;

   拼接的sql语句就变为了:

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

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

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

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

   SELECT title,content FROM news WHERE id = ?;
   # 这种方式在最后运行的时候会将 “id=1 and 1=2 UNION SELECT username, password FROM admin” 当成一个整体拼在sql中,大概是如下这种效果
    SELECT title,content FROM news WHERE id = ‘id=1 and 1=2 UNION SELECT username, password FROM admin’;

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

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

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

  • 2.1 模糊查询like

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

    select * from news where title like%#{title}%’;

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

    select * from news where title like%${title}%’;

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

  • 2.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注入漏洞。

  • 2.3 order by之后

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

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

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

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

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

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

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

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

  select * from news where tile like concat(‘%’,#{title}, ‘%’);

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

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

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

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

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

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

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

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

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