【SQL】SQL注入是什麼?如何防止SQL注入?
今天在優化一個查詢的時候,關於一個SQL語句的問題,發現引數傳進去是空的,導致條件查詢失敗了,查出來的是所有的資料。
剛開始是這樣寫的
<select id="selectPictureList" parameterType="map" resultMap="BaseResultMap">
select * from picture
<where>
<if test="pictureName != null and pictureName != ''">
picture_name like '%${pictureName,jdbcType=VARCHAR}%'
</if>
<if test="pictureOwner != null and pictureOwner != ''">
and picture_owner = #{pictureOwner,jdbcType=VARCHAR}
</if>
</where>
</select>
控制檯列印的資訊是:
Preparing: select * from picture WHERE picture_name like '%%' LIMIT ?
DEBUG [http-apr-8080-exec- 5] - ==> Parameters: 10(Integer)
DEBUG [http-apr-8080-exec-5] - <== Total: 4
這裡也確定引數是傳到controller方法裡面的,後來仔細檢查之後,仍然沒有看出來哪裡有問題,於是開始百度,對比之後發現sql拼接的時候,在大括號裡多了一個引數的型別,jdbcType=VARCHAR
,將這個刪掉之後,條件查詢就生效了。事情到這裡並沒有結束,我看到有人提出這樣寫不能防止sql注入的問題,看到這裡有點蒙,之前確實聽說過在SQL拼接時,要注意防止SQL注入的問題,但是什麼是SQL注入,我現在真的忘了。。。。。。
於是開始查SQL注入的問題:
下面內容為參考:https://www.cnblogs.com/sdya/p/4568548.html 大神的部落格整理,
這篇部落格,介紹的也很詳細:https://blog.csdn.net/lxzpp/article/details/80521332
何為SQL注入?
所謂SQL注入,就是通過把SQL命令插入到Web表單遞交或輸入域名或頁面請求的查詢字串,最終達到欺騙伺服器執行惡意的SQL命令,比如先前的很多影視網站洩露VIP會員密碼大多就是通過WEB表單遞交查詢字元暴出的,這類表單特別容易受到SQL注入式攻擊.
用我自己的理解就是:
比如從登入模組來看,我們在將使用者輸入的條件在後臺拼接成動態SQL查詢時,如果使用者按照我們設想的那樣,使用者名稱和密碼都是正常的字串,那麼我們後臺寫的查詢使用者的語句:select * from user where user_name = '使用者名稱' and password = '密碼';
便可以正常確認是否存在此使用者的資訊。
但是這裡怎麼會出現SQL注入問題呢?
如果我們在使用者名稱的輸入框裡輸入的是:user ' or 1 = 1#
的情況下,後臺在執行這句SQL的時候,將將自動拼接成了select * from user where user_name = ‘user ' or 1 = 1#' and password = '密碼'
’`
而在mysql中,SQL語句中#後面的被當成註釋了(在Oracle中--
後面是註釋),而前面的查詢中有一個恆成立的條件1=1
這就導致這條SQL語句查詢的使用者不是為空的,是存在的。此時便可以騙過伺服器,模擬登陸成功的場景。
還有一種情況是在刪除操作的時候,一般我們進行刪除資料的時候,都是在url裡拼接出要刪除的資料的id什麼的,delUserById.do?userId=1
這時,在不懷好意的人獲取到請求連線之後,在加上一個小小的or,比如這樣:userId=2 or 1
這樣就有可能刪除全部資料------sql注入就是通過類似的手段來破壞資料
看到這裡我是忽然感覺後背一涼,原來我天天自認為自己操作簡單的增刪改查已經很熟練了,但是這些漏洞卻從來沒有注意過,如果這種災難要是出現在專案中,可能就要捲鋪蓋走人了吧。。。。。
由此我們在設計SQL語句的時候就應該試著去避免這些坑。
設計SQL時,應避免SQL注入
對於上面的例子中,我們改如何改進呢?
- 換一種方式實現SQL的動態拼接,將模糊條件查詢寫成這樣
<where>
<if test="pictureName != null and pictureName != ''">
picture_name like "%"#{pictureName}"%"
</if>
</where>
此時控制檯列印的SQL語句為:
DEBUG [http-apr-8080-exec-9] - ==> Preparing: select * from picture WHERE picture_name like "%"?"%" LIMIT ?
DEBUG [http-apr-8080-exec-9] - ==> Parameters: 微信頭像(String), 10(Integer)
DEBUG [http-apr-8080-exec-9] - <== Total: 1
可見這樣寫直接將傳來的條件作為一個String引數拼接到SQL語句中,這樣就可以避免SQL注入的問題了。
- 使用SQL中的字串拼接函式
CONCAT
可將like語句寫成:like CONCAT (CONCAT('%', #{name}, '%'))
<where>
<if test="pictureName != null and pictureName != ''">
picture_name like CONCAT(CONCAT('%', #{pictureName}, '%'))
</if>
</where>
DEBUG [http-apr-8080-exec-1] - ==> Preparing: SELECT count(0) FROM picture WHERE picture_name LIKE CONCAT(CONCAT('%', ?, '%'))
DEBUG [http-apr-8080-exec-1] - ==> Parameters: 微信頭像(String)
DEBUG [http-apr-8080-exec-1] - <== Total: 1
這兩種方式都是直接從SQL語句中著手,避免了SQL注入。
- 除了直接優化SQL語句,還有一種解決辦法是,在後臺對傳來的引數進行校驗,使用正則表示式。
比較通用的一個方法:(||之間的引數可以根據自己程式的需要新增)
public class TestSQL {
public static boolean sql_inj(String str)
{
String inj_str = "'|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|,";
String inj_stra[] = inj_str.split("|");
for (int i = 0; i < inj_stra.length; i++)
{
if (str.indexOf(inj_stra[i]) != 0)
{
return true;
}
}
return false;
}
}