mybatis如何预防不经意间的SQL注入
2021-12-10 本文已影响0人
小胖学编程
SQL注入的危害就不多说,但是如何在日常开发中预防SQL注入?
1. 理论
- 什么是SQL注入
注入攻击的本质,是程序把用户输入的数据当做代码执行。这里有两个关键条件:
第一是用户能够控制输入;
第二是用户输入的数据被拼接到要执行的代码中从而被执行。
sql注入漏洞则是程序将用户输入数据拼接到了sql语句中,从而攻击者即可构造、改变sql语义从而进行攻击。
- SQL漏洞一般的预防措施:
- 对于接受外部参数的动态SQL,能够进行预编译操作的地方一律使用预编译,禁止直接从字符串拼接SQL;
- 对于SQL不能预编译的地方,例如order by $param处,应该使用严格的白名单进行校验,然后拼接参数;
- mybatis动态sql的两种方式
动态 sql 是 mybatis 的主要特性之一,在mybatis中我们可以把参数传到xml文件,由mybatis对sql及其语法进行解析,mybatis支持使用${}和#{}。
#{}:在mybatis中是预编译操作;
${}:在mybatis中是直接拼接;
- 为什么#{}可以预发SQL注入
直观的理由:
使用${}方式传入的参数,mybatis不会对它进行特殊处理,而使用#{}传进来的参数,mybatis默认会将其当成字符串。
例如:selec * from #{table};
解析后:select * from "test";
例如:selec * from ${table};
解析后:select * from test;
因为前者多了字符串的引号,那么可以预防sql注入。
书面化的理由是:
#和$在预编译处理中是不一样的。#类似jdbc中的PreparedStatement,对于传入的参数,在预处理阶段会使用?代替,待真正查询的时候,即在数据库管理系统中(DBMS)才会代入参数。而${}只是简单的替换。
2. 实践
2.1 一般sql的处理
核心:能够进行预编译的地方,一律使用#{}进行预编译。
在where接受参数:
<select id="selectField">
SELECT field_name FROM table_name WHERE 1 = 1
<if test="condition != null">
AND field_name = #{condtion} <!--禁止使用${param}拼接-->
</if>
</select>
在like接受参数:使用CONCAT函数。
<select id="selectByLike">
SELECT field_name FROM table_name WHERE 1 = 1
<if test="condition != null">
AND field_name like CONCAT(#{condition}, '%') <!--禁止使${param}拼接-->
</if>
</select>
在in处接受参数:
<select id="selectInSQL">
SELECT field_name FROM table_name WHERE 1 = 1
<if test="conditionArray != null">
field_name in
<!--禁止使用${param}拼接-->
<foreach collection="conditionArray" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
</select>
2.2 order by的处理
核心:不能使用#{}进行预编译时,在mybatis进行白名单校验。
<select id="sortSQL">
SELECT field_name FROM table_name WHERE 1 = 1
<!--传入的字段不为空,且传入的字段为name,才执行`order by name`的sql-->
<if test="columnName != null and columnName=='name'.toString()">
order by name
<if test="orderName !=null and orderName=='desc'.toString()">
desc
</if>
<if test="orderName !=null and orderName=='asc'.toString()">
asc
</if>
</if>
</select>
实战.png