SQL注入

1:  什么是SQL注入?

String sql = "select * from user_table where username='"+userName+" ' and password=' "+password+" '";
userName和password是前台传进来的,如果这里 
userName = 'or 1 = 1 #
password = 

那么SQL语句就是: SELECT * FROM user_table WHERE username=''or 1 = 1 # and password='’
上面的#后面的就被注释掉了(mysql中的#是注释符号; SQL server里是--)

(补充:SQL 的转义字符是:'(单引号) 例:select * from tbl where uyear='''06'

请注意其中红色背景的单引号,它即表示转义字符,如果我们省略,则整个语句会出错,转义字符不会输出,上例中 uyear 的实际条件值为 '06,而不是 ''06

为什么不能省略呢,假如我们省略,上句变成:select * from tbl where uyear=''06'由于在 SQL 中单引号表示字符串的开始和结束符号,于是 SQL 解释器会认为语句中灰色背景的为字符串,其后的语句显然是个错误的语句,当然会报错,为了解决字符串的单引号问题,就出现了转义字符单

2:  Spring data Jpa, Spring JDBC, Spring dao 如何防止SQL注入?

           各种ORM都支持参数化查询,一般的查询都用参数化的方法就可以防止注入。

         (1) 参数化查询,而不是直接的拼接的SQL 文本查询(plain sql)。数据库系统都提供SQL语句的预编译(prepare)和查询参数绑定功能,在SQL语句中放置占位符'?',然后将带有占位符的SQL语句传给数据库编译,执行的时候才将用户输入的数据作为执行的参数传给用户。这样的操作不仅使得SQL语句在书写的时候不再需要拼接,看起来也更直接,而且用户输入的数据也没有机会被送到数据库的SQL解释器被编译执行,也不会越权变成代码。

            绑定变量使用预编译语句是预防SQL注入的最佳方式.

           (2) 用正则过滤

         (3)字符串过滤,有一些特殊情况,不能用参数化查询的方式,需要过滤。

                   比如Spring工具包的StringEscapeUtils.escapeSql()方法对参数进行过滤. 主要是过滤单引号和注释。

3: mybatis是如何防止SQL注入?

(1). 使用#{}格式的语法在mybatis中使用Preparement语句来安全的设置值,执行sql类似下面的:

PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1,id);

这样做的好处是:更安全,更迅速,通常也是首选做法。

(2). 不过有时你只是想直接在 SQL 语句中插入一个不改变的字符串。比如,像 ORDER BY,你可以这样来使用:

ORDER BY ${columnName}

此时MyBatis 不会修改或转义字符串。

这种方式类似于:

    Statement st = conn.createStatement();
       
      ResultSet rs = st.executeQuery(sql);

这种方式的缺点是: 以这种方式接受从用户输出的内容并提供给语句中不变的字符串是不安全的,会导致潜在的 SQL 注入攻击,因此要么不允许用户输入这些字段,要么自行转义并检验。

【结论】在编写MyBatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止SQL注入攻击。

#{}:相当于JDBC中的PreparedStatement

${}:是输出变量的值

简单说,#{}是经过预编译的,是安全的${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入。

下边的例子是用$而不用#的例子:

<if test="map.ColumnNameSort!=null and map.ColumnNameSort!=''"> 
  ORDER BY columnName #{map.ColumnNameSort} 
</if>

  ColumnNameSort即前端传的排序方式,asc或者desc。

  然后,预计它的输出应该是类似于下面这样的

ORDER BY columnName desc

  但是,真正跑起来时,排序的效果一直没出现,经常一番查找,发现是mybatis 的’#{}’传值的问题,它将sql语句编译成了如下

ORDER BY columnName 'desc' 或者 ORDER BY columnName 'asc'

  这样,desc或者asc就成了字符串而不是关键字,sql语句的意思是columnName的别名是desc或者asc,没加排序关键字时默认是正序排序,成了如下

ORDER BY columnName "desc" asc 或者 ORDER BY columnName "asc" asc

  排序没效果的问题找到原因了,解决之,mybatis提供了另一种绑定参数的方式–${param},将sql配置改为

ORDER BY columnName ${map.ColumnNameSort}

  这样一来,mybatis会直接将ColumnNameSort的值加入sql中,不会转义。正确结果:

ORDER BY columnName desc

  最后,对于mybatis中#和$绑定参数的区别做个总结,避免以后类似的问题发生。

 

  • #{}将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:order by #{id},如果传入的值是111,那么解析成sql时的值为order by “111”, 如果传入的值是id,则解析成的sql为order by “id”。
  • ${}将传入的数据直接显示生成在sql中。如:order by 
  • ${id},如果传入的值是111,那么解析成sql时的值为order by 111, 如果传入的值是id,则解析成的sql为order 
  • by id。
  • #方式能够很大程度防止sql注入。
  • $方式无法防止Sql注入。
  • $方式一般用于传入数据库对象,例如传入表名.
  • 一般能用#的就别用$.
posted @ 2018-03-04 22:16  刘大飞  阅读(444)  评论(0编辑  收藏  举报