MyBatis如何防SQL注入

MyBatis如何防SQL注入:

这一节来讲下MyBatis的防SQL注入,SQL注入大多数也会比较清楚,就是SQL参数对应的字段值时插入混合SQL,如 ** username = or 1= 1** 这种,如果有更恶劣的,带上drop database 这种都是有可能的,所以一般SQL都会进行一定防注入处理,MyBatis其实用法大都清楚,就是**#{paras}和${paras}**两种用法,以前我就是会用,但是具体原理咱也没看过,在一次面试中,被别人问到过具体用法,注入原理,处理原理等等,当时就是根据自己的印象去回答了,但是对于MyBatis与数据库具体处理SQL注入却不是很熟悉的,这一节就来详细讲解下,记录一下。

#1. #{paras}与${paras}用法

对于**#{paras}和KaTeX parse error: Expected 'EOF', got '#' at position 23: …**写两个用法吧,一个根据**#̲{paras}**来查询,一种…{paras}**来查询,如下:

调用这两个方法的程序为:

运行结果为:

运行结果和想象中差不多,在conf.xml中添加打印sql的配置.

第一个sql是进行了占位符处理,第二个是直接拼出了sql语句。

上述就是演示#{paras}调用和和sql注入的过程,下面开始讲两者在实现过程中的原理。

2. 初始化源码分析

首先看看这两个方法是怎么加载的,这还是要回到注解方法的解析中,在前文中我们知在解析注解时,会将当前sql包装成SqlSource对象,然后在查询时使用,代码如下:

我们进getSqlSourceFromAnnotations方法中看看:

 

此处我们只分析带有@Select注解的方法,所有就是进入if条件中进行处理,这里再进入buildSqlSourceFromStrings方法。

 

最后通过languageDriver返回SqlSource,进入createSqlSource方法中:

在两个方法中做了最后的sql包装,在createSqlSource方法中,第一种是处理带有**

public boolean isDynamic() {
 DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
 GenericTokenParser parser = createParser(checker);
 parser.parse(text);
 return checker.isDynamic();
 }

这里实例化了一个DynamicCheckerTokenParser对象,但是进这个实例化方法中可以看到并没有做什么事情,主要是createParser中,转到createParser方法。

 

然后下一步对是否有"${", "}"进行判断,parser.parse(text);,最后返回是否返回是否是动态sql,在这毫无疑问的是我们前面写的方法中,第一个是返回的RawSqlSource对象,第二个返回的是DynamicSqlSource对象,这两个对象这里不做分析,等会再执行时再来看是如何对sql进行处理的。

3. mapper执行源码分析

上面分析完了在配置时的sql过程,现在分析在select语句执行时对SqlSource的解析,在前面文章分析中,我们知所有的查询基本是调用的SelectList方法,然后在最后调用doQuery方法之前会获得BoundSql对象,我们的源码分析就是从这开始。

 

 

这里可以对上面两个代码调试一下代码,BoundSql对象如下图:

方法1:

 这里#{paras}已经替换成了占位符,然后对应参数映射在parameterObject对象中,这里就先不展示第二个方法执行到这的BoundSql对象了,先把这个分析清楚吧,从上面图与源码中我们知最终的处理方法在,ms.getBoundSql中,进入到getBoundSql方法。

进入到sqlSource.getBoundSql(parameterObject);方法中,在前文中我们只分析了DynamicSqlSource,但是并没有对RawSqlSource进行分析,在这顺便说下RawSqlSource,直接看RawSqlSource的构造方法。

对sql进行了进一步的解析,进入到parse方法中。 

此处判断sql中是否含有"#{""}",parser.parse(originalSql)中解析源码的过程比较复杂,没有太多可以说的,但是得说下占位符的替换,在替换参数是最终会调用到SqlSource中的handlerToken方法。

此处进行了参数的替换,以及对字段的映射。

然后最终返回StaticSqlSource对象,所以此处RawSqlSource最后还是返回StaticSqlSource对象,然后在StaticSqlSource对象中执行getBoundSql方法,可以进StaticSqlSource的getBoundSql中看看。

最终对替换参数后的sql、参数、参数映射以及对应值,构造BoundSql对象返回。

此处几乎分析的还是加载配置时的代码,还是回到query执行处,在获取到boundSql对象后,继续执行query方法,最终调用PreparedStatement对象执行execute方法,这里传入的sql是需要进行预编译的sql,同时传入参数,因此此处参数不会和sql一起执行,纯粹是sql对应查询字段值。

再来分析动态sql,再次回到query方法,如下图:

此处已经是拼好的sql,但是我们看ms中的SqlSource对象。

 

此时还没有对sql进行拼接,所以在获取BoundSql时,对参数就进行了替换,在上文中我们知此时的SqlSource对象为DynamicSqlSource对象,直接进入getBoundSql方法中。

此处构建DynamicContext上下文,最终处理还是在apply方法中。

 

在apply方法中,对text进行处理,核心代码在parser.parse(text)中,这里处理的逻辑就不讲了,进去后可以看到对参数的替换最终组装成当前的sql,从而实现了sql 的注入。

在后面的query方法就不再进行分析了,基本在组装出sql后一切都明了了。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章