1. 程式人生 > >MyBatis如何防止SQL註入

MyBatis如何防止SQL註入

用戶輸入 數據庫服務 update pub 輸出 正則 ont 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|-|+|,等

MyBatis如何防止SQL註入