MyBatis如何防止SQL注入

SQL注入起因

SQL注入是一种常见的攻击方式,攻击者或者误操作者通过表单信息或者URL输入一些异常的参数,传入服务端进行SQL处理,可能会出现这样的情况delete from app_poi where poi_id = (输入参数):

输入参数:10 or 1 = 1

SQL拼接:delete from app_poi where poi_id = (10 or 1 = 1)

执行结果:app_poi中的所有数据都会被delete

这种问题出现的原因可能是攻击者故意为之,也可能是误操作,那么服务端该如何处理,避免这样的问题出现呢?

MyBatis

MyBatis是一种半自动化持久化框架,所有SQL操作都需要我们通过配置文件或者注解的方式手动编辑,它提供了一种类函数的功能,用户提供输入,MyBatis根据输入类型和输入参数进行验证,如果参数没有问题,那么MyBatis就针对合法的输入提供输出,即

用户输入参数+用户输入类型 ---> MyBatis检测 ---> 返回输出

接下来通过配置文件和注解两种方式来进行说明

  • 配置文件:
<delete id="deletePoiById" parameterType="java.lang.Integer">
    DELETE
    FROM app_poi
    where poi_id = #{poiId}
</delete>
  • 基于注解
@delete("delete from app_poi where poi_id = #{poiId}")
public void deletePoiById(@Param("poiId") Integer poiId);

MyBatis对用户输入的数据会检测其类型是否和parameterType标识的类型一致,如果一致,则进行后续拼接处理,否则抛出异常,SQL就不会执行

对于10 or 1 = 1这种输入,和java.lang.Integer类型不同,就会abort并抛出异常

原理

MyBatis使用#{}防止SQL注入,其实使用的是PreparedStatement,PreparedStatement会对执行SQL进行预编译,这个过程是发生在数据库服务端,也就是说,对于PreparedStatement的SQL需要两次网络请求,首次是获取PreparedStatement,第二次才是发起SQL执行;

PreparedStatement会对SQL中输入的参数进行检测,并在SQL都是用问号来设置占位符,即只允许传入一个参数,像(10 or 1 = 1)这种明显不会被占位符所接受

String类型SQL注入

对于数字类型参数#{}可以很好的解决SQL注入,其原因不难理解,那么对于string类型的呢?Mybatis只是使用了一个占位符并不能解决该问题

<delete id="deletePoiById" parameterType="java.lang.String">
    DELETE
    FROM app_poi
    where content = #{content}
</delete>

如果content的内容是“美食 or 1 = 1”,那么数据将会被全部清除

解决该问题,还是结合传统方式,使用正则表达式匹配,总结常见的需要摒弃的string,替换为空即可,例如string包含

and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|; |or|-|+|,等

posted @ 2017-09-25 15:21  HE_PX  阅读(5075)  评论(0编辑  收藏  举报