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後一切都明瞭了。

 

 

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