1. 程式人生 > >【SQL】SQL注入是什麼?如何防止SQL注入?

【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注入

對於上面的例子中,我們改如何改進呢?

  1. 換一種方式實現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注入的問題了。

  1. 使用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注入。

  1. 除了直接優化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;
	}
}