mybatis系列-sql注入

Posted LittleJAVA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis系列-sql注入相关的知识,希望对你有一定的参考价值。

  1. 目标 

    1. 了解什么是sql注入 

    2. mybatis中如何防止sql注入的? 

  2. 什么是sql注入? 

 

今天来介绍一个编程中直接可以忽略的问题,那就是sql注入。那么,什么是sql注入呢?我的理解是,在你写得sql中填充的变量超出了你的逾期,你也不知道sql最终会查出什么东西。这就存在问题。例如sql语句select * from table where v = p1。当你传p1=1时,select * from table where v=1是你期望的sql。但是,当你p1='1 or true',这样传进来的是,那么,sql会是select* from table where v=1 or true就会返回所有的数据。这样就会出现你意料之外的事情。这就是锅的起源。这里可以总结两点 

  1. 参数类型是弱类型的,那么坑比较大(phpjavascript中比较容易出问题,java应该还行) 

  2. 参数可以传任意的字符串 

那么,如何去解决这个问题?我的参数就是字符串不行吗? 

 

我是java工程师,那么先不去管phpjavascript如何解决的。前面之所以说可以忽略的问题,是因为我们在开发中的sql处理都是用了PrepareStatement。用了它就不会 

注入。具体什么原理,暂时还不清楚…… 

 

但是java开发的框架比较多,就是不用jdbc来弄sql。他们喜欢用mybatis的框架。那么,mybatis是如何来处理防止sql注入。答案是,利用jdbcPrepareStatement。不知道猜的对不对? 

 

  1. mybatis配置文件中#{}${}的区别 

 

答案如下: 


sql注入 

应用场景 


#{} 

 

参数 


${} 

 

表名的替换,库名 


 

假如就这样也就没有必要写了,下面来考虑具体如何实现的?在这之前可以先想想让你实现的话,你会怎么处理? 

 

  1. mybatis源码解析 

 

如何通过源码去解决我碰到的问题呢?我的过程如下: 

  • 框架的启动过程 

  • 关键字出现的地方,什么时候会被调用,寻找合理的debug点去验证 

 

例如我拿到如下mapper文件,我首先会考虑@Select注解会在哪里被调用, 

然后找到了MapperRegistry类,它主要三个方法getMapperaddMapperhasMapper。在addMapper时,会利用MapperAnnotationBuilder去解析BlogMapper类中的@Select注解。 

 

  • 解析mapper方法上的annotation 

 

mybatis系列-sql注入

 

org.apache.ibatis.builder.annotation.MapperAnnotationBuilder中注册的注解 

mybatis系列-sql注入

然后获取注解的特殊信息 

mybatis系列-sql注入

 

  • SqlSource的构建 

 

在验证注解的方式利用mapper的话,可以验证这个过程主要返回什么内容,这个方法的主要目的是,根据mapper中某个方法上的注解来构造SqlSource;不过这个SqlSource的创建依赖LanguageDriver。为什么会这样?为了扩展。 

mybatis系列-sql注入

 

终于到了sql解析的过程了,这里sql解析主要做了什么事情? 

mybatis系列-sql注入

 

  • PropertiesParser解析${} 

 

PropertiesParser主要解决了配置的properties变量是否在sql中有匹配。有匹配的话,就把它替换掉。写了个验证程序。如下: 

mybatis系列-sql注入

 

mybatis系列-sql注入

 

那么,他是如何实现的呢? 

mybatis系列-sql注入

如何判断动态性?同理如上 

 

  • SqlSourceBuilder解析#{} 

 

主要将#{}中的参数,替换为“?” 

mybatis系列-sql注入

 

mybatis系列-sql注入

 

  • mybatis中sql调用过程如何处理#{}${}的字符 

 

框架处理一个要分析两个过程,一个是初始化过程?初始化后,调用的过程是怎么样?上面分析了SqlSource的构建过程,下面如何考虑mybatis的执行过程。所有的执行都需要建立一个SqlSession,那么,执行的过程就从SqlSession来开始。mybatis中默认实现SqlSession的是DefaultSqlSession。我从SqlSession#selectOne开始分析。 

 

mybatis系列-sql注入

 

在这之前,很困惑的是selectOnestatement中是什么样的值?#{}的字符串有没有被“?”替代。这里可以debug下,来验证。同时可以查看selectOne的调用。这个调用的入口是什么?而根据SqlCommand的定义,这个语句的值可以是mapperInterface.getName() + "." + methodName; 

 

mybatis系列-sql注入

 

通过根据selectOne的源码,最终的结果是获得一个MappedStatement,这个对象缓存在Configuration对象之中。然后调用Executor来执行,最终会选择一个 

特殊的StatementHandler来进行,其中PreparedStatementHandler就是防sql注入。这里有个疑问,在前面初始化SqlSource的时候,存在为被替换的${},将在什么地方做替换,替换的数据从哪里处理?在TextSqlNode中处理,后续再深入。 

 

 

 

  1. 总结 

 

此次的mybatis的源码分析主要为了了解mybatis如何防止sql注入。本质是利用jdbcPreparedStatement对象,利用其预编译机制。同时,在逐步分析代码的过程中有如下收获: 

  • 认识mybatis如下几个概念,可以模块化分解mybatis的各个过程 

    • SqlSource 

    • BoundSql 

    • MappedStatement 

    • Executor 

    • Configuration 

    • StatementHandler 

    • MapperRegistry 

  • 理解了mybatisAnnotation的解析机制,跟spring中的处理方式有些许不同 

 

同时,还有另外一部分可以深入了解下,那就是dynamicSQL是如何解析?这个考虑在下次blog中分享 

 

 

 


以上是关于mybatis系列-sql注入的主要内容,如果未能解决你的问题,请参考以下文章

Java编程系列Mybatis的Interceptor注入yml自定义变量,多种实现方式

Java编程系列Mybatis的Interceptor注入yml自定义变量,多种实现方式

MyBatis系列:简介和下载

[转]狂神说SSM框架系列连载

MyBatis-Plus 3.0.3 Sql注入器添加,即全局配置Sql注入器,sqlInjector改写

Mybatis 系列9