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|-|+|,等