五月天青色头像情侣网名,国产亚洲av片在线观看18女人,黑人巨茎大战俄罗斯美女,扒下她的小内裤打屁股

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

SQL注入詳解

2023-07-12 22:37 作者:Mr-白帽  | 我要投稿

一、什么是SQL注入

SQL注入(SQL lnjection)是發(fā)生在Web程序中數(shù)據(jù)庫層的安全漏洞,是比較常用的網(wǎng)絡(luò)攻擊方式之一,他不是利用操作系統(tǒng)的BUG來實現(xiàn)攻擊,而是針對程序員編寫時的疏忽,通過SQL語句,實現(xiàn)無賬號登錄,甚至修改數(shù)據(jù)庫。也就是說,SQL注入就是在用戶輸入的字符串中添加SQL語句,如果在設(shè)計不良的程序中忽略了檢查,那么這些注入進去的SQL語句就會被數(shù)據(jù)庫服務(wù)器誤認為是正常的SQL語句而運行,攻擊者就可以執(zhí)行計劃外的命令或者訪問未授權(quán)的數(shù)據(jù)。


二、SQL注入的原理

1.惡意拼接查詢

SQL語句可以對數(shù)據(jù)進行增刪改查,且使用分號來分隔不同命令。例如:

SELECT * FROM user WHERE user_id = $user_id

?其中user_id是傳入的參數(shù),如果傳入?yún)?shù)的值為"1234;DELETE FROM user",那么最終執(zhí)行的查詢?yōu)椋?/p>

SELECT * FROM users WHERE user_id = 1234; DELETE FROM users

如果執(zhí)行以上語句,則會刪除user表中的所有數(shù)據(jù)

2.利用注釋執(zhí)行非法命令

?SQL語句中可以插入注釋,例如

SELECT COUNT(*) AS 'num' FROM score WHERE id=24411 AND version=$version

如果version包含了惡意的字符串" '-1' OR 3 AND SLEEP(500) ",那么最終查詢的語句會變?yōu)?/p>

SELECT COUNT(*) AS 'num' FROM score WHERE id=24411 AND version='-1' OR 3 AND SLEEP(500)

以上惡意查詢只是想耗盡系統(tǒng)資源,SLEEP(500) 將導(dǎo)致 SQL 語句一直運行。如果其中添加了修改、刪除數(shù)據(jù)的惡意指令,那么將會造成更大的破壞。

3.傳入非法參數(shù)

SQL 語句中傳入的字符串參數(shù)是用單引號引起來的,如果字符串本身包含單引號而沒有被處理,那么可能會篡改原本 SQL 語句的作用。 例如:

SELECT * FROM user_name WHERE user_name = $user_name

如果 user_name 傳入?yún)?shù)值為 G'chen,那么最終的查詢語句會變?yōu)椋?/p>

SELECT * FROM user_name WHERE user_name ='G'chen'

一般情況下,以上語句會執(zhí)行出錯,這樣的語句風(fēng)險比較小。雖然沒有語法錯誤,但可能會惡意產(chǎn)生 SQL 語句,并且以一種你不期望的方式運行。

4.添加額外條件

在 SQL 語句中添加一些額外條件,以此來改變執(zhí)行行為。條件一般為真值表達式。例如:

UPDATE users SET userpass='$userpass' WHERE user_id=$user_id;

如果 user_id 被傳入惡意的字符串“1234 OR TRUE”,那么最終的 SQL 語句會變?yōu)椋?/p>

UPDATE users SET userpass= '123456' WHERE user_id=1234 OR TRUE;

如果執(zhí)行以上語句,將更改所有用戶的密碼

三、SQL注入實例


四、如何避免SQL注入

1.過濾輸入內(nèi)容,校驗字符串

過濾輸入內(nèi)容就是在數(shù)據(jù)提交到數(shù)據(jù)庫之前,就把用戶輸入中不合法的字符剔除掉,可以使用編程語言提供的處理函數(shù)或自己的處理函數(shù)來進行過濾,還可以使用正則表達式匹配安全的字符串。

如果值屬于特定的類型或有具體的格式,那么在拼接SQL語句之前就要進行校驗,驗證其有效性,比如對于某個傳入的值,如果可以確定是整形,則要判斷他是否為整型,在瀏覽器端(客戶端)和服務(wù)器端都要進行驗證。

2.參數(shù)化查詢(綁定變量,使用預(yù)編譯查詢)

參數(shù)化查詢目前被視為預(yù)防SQL注入最有效的方法,參數(shù)化查詢是指在設(shè)計與數(shù)據(jù)庫連接并訪問數(shù)據(jù)時,在需要填入數(shù)值或者數(shù)據(jù)的地方,使用參數(shù)(Parameter)來給值

MySQL 的參數(shù)格式是以"?"字符加上參數(shù)名稱而成,如下所示:

UPDATE myTable SET c1 = ?c1, c2 = ?c2, c3 = ?c3 WHERE c4 = ?c4

在使用參數(shù)化查詢的情況下,數(shù)據(jù)庫服務(wù)器不會將參數(shù)的內(nèi)容視為SQL語句的一部分來進行處理,而是在數(shù)據(jù)庫完成SQL語句的編譯之后,才 套用參數(shù)運行,因此就算參數(shù)含有破壞性的指令,也不會被數(shù)據(jù)庫所運行。

使用預(yù)編譯的SQL語句語義不會發(fā)生改變,在SQL語句中,變量用問號?表示,黑客即使本事再大,也無法改變SQL語句的結(jié)構(gòu)

簡單總結(jié),參數(shù)化能防注入的原因在于,語句是語句,參數(shù)是參數(shù),參數(shù)的值并不是語句的一部分,數(shù)據(jù)庫只按語句的語義跑,至于跑的時候是帶一個普通背包還是一個怪物,不會影響行進路線,無非跑的快點與慢點的區(qū)別。

3.安全測試,安全審計

?除了開發(fā)規(guī)范,還需要合適的工具來確保代碼的安全。我們應(yīng)該在開發(fā)過程中應(yīng)對代碼進行審查,在測試環(huán)節(jié)使用工具進行掃描,上線后定期掃描安全漏洞。通過多個環(huán)節(jié)的檢查,一般是可以避免 SQL 注入的。

有些人認為存儲過程可以避免 SQL 注入,存儲過程在傳統(tǒng)行業(yè)里用得比較多,對于權(quán)限的控制是有一定用處的,但如果存儲過程用到了動態(tài)查詢,拼接 SQL,一樣會存在安全隱患。

下面是在開發(fā)過程中可以避免 SQL 注入的一些方法。

?3.1.避免使用動態(tài)SQL

避免將用戶的輸入數(shù)據(jù)直接放在SQL語句中,最好使用準(zhǔn)備好的語句和參數(shù)化查詢,這樣更安全。

3.2.不要將敏感數(shù)據(jù)保留在純文本中

加密存儲在數(shù)據(jù)庫中的私有/機密數(shù)據(jù),這樣可以提供了另一級保護,以防攻擊者成功的排出敏感數(shù)據(jù)

3.3.限制數(shù)據(jù)庫的權(quán)限和特權(quán)

將數(shù)據(jù)庫用戶的功能設(shè)置為最低要求;這將限制攻擊者在設(shè)法獲取訪問權(quán)限時可以執(zhí)行的操作

3.4.避免直接向用戶顯示數(shù)據(jù)庫錯誤

攻擊者可以使用這些錯誤消息來獲取有關(guān)的數(shù)據(jù)庫信息。

四、SQL預(yù)編譯

1.預(yù)編譯語句是什么?

通常我們的一條sql在db接受到最終執(zhí)行完畢返回可以分為下面三個過程:

? ? ? ? 1.詞法和語義的解析

? ? ? ? 2.優(yōu)化sql語句,制定執(zhí)行計劃

? ? ? ? 3.執(zhí)行并返回結(jié)果

這種普通語句被稱為Immediate Statements

但是很多情況,我們的一條sql語句可能會反復(fù)執(zhí)行,或者每次執(zhí)行的時候只有個別的值不同(比如query的where子句值不同,update的set子句值不同,insert的values值不同)。

如果每次都需要經(jīng)過上面的詞法語義解析、語句優(yōu)化、制定執(zhí)行計劃等,則效率就明顯不行了。

所謂預(yù)編譯語句就是將這類語句中的值用占位符替代,可以視為將sql語句模板化或者說參數(shù)化,一般稱這類語句叫Prepared Statements或者Parameterized Statements

預(yù)編譯語句的優(yōu)勢在于歸納為:一次編譯、多次運行,省去了解析優(yōu)化等過程;此外預(yù)編譯語句能防止sql注入。

當(dāng)然就優(yōu)化來說,很多時候最優(yōu)的執(zhí)行計劃不是光靠知道sql語句的模板就能決定了,往往就是需要通過具體值來預(yù)估出成本代價。

2.預(yù)編譯語句

(1)建一張測試表t


(2)編譯

通過 PREPARE stmt_name FROM perpare_stm 的語法來預(yù)編譯一條sql語句

PREPARE ?ins FROM 'INSERT INTO t SELECT ?,?';

(3)執(zhí)行

通過 EXECUTE stmt_name [USING @var_name [,@var_name]...]的語法來執(zhí)行預(yù)編譯語句

SET @a=999,@b='hello';

EXECUTE ins USING @a,@b;

此時數(shù)據(jù)已經(jīng)插入。

MySQL中的預(yù)編譯語句作用域是session級,但我們可以通過max_prepare_stmt_count變量來控制全局最大的存儲的預(yù)編譯語句

SET @@global.max_prepared_stmt_count=1;

/*此時設(shè)置預(yù)編譯最大條數(shù)為1,如果繼續(xù)使用預(yù)編譯,就會報錯*/

(4)釋放

如果我們想要釋放一條預(yù)編譯語句,則可以使用{DEALLOCATE | DROP} PREPARE stmt_name的語法進行操作

?DEALLOCATE prepare ins;

五、MyBatis如何預(yù)防SQL注入

觀察兩段代碼區(qū)別:


<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">

select id, username, password, role

from user

where username = #{username,jdbcType=VARCHAR}

and password = #{password,jdbcType=VARCHAR}

</select>

?

<select id="selectByNameAndPassword" parameterType="java.util.Map" resultMap="BaseResultMap">

select id, username, password, role

from user

where username = ${username,jdbcType=VARCHAR}

and password = ${password,jdbcType=VARCHAR}

</select>

mybatis中#和$的區(qū)別:

? ?  1、#將傳入的數(shù)據(jù)都當(dāng)成一個字符串,會對自動傳入的數(shù)據(jù)加一個雙引號。

如:where username=#{username},如果傳入的值是111,那么解析成sql時的值為where username="111", 如果傳入的值是id,則解析成的sql為where username="id". 

  2、$將傳入的數(shù)據(jù)直接顯示生成在sql中。

如:where username=${username},如果傳入的值是111,那么解析成sql時的值為where username=111;

如果傳入的值是;drop table user;,則解析成的sql為:select id, username, password, role from user where username=;drop table user;

? ? ? ? 3、#能很大程度防止sql注入,$ 方式無法防止sql注入

? ? ? ? 4、$方式一般用于傳入數(shù)據(jù)庫對象,例如傳入表名.

   5、一般能用#的就別用$,若不得不使用“${xxx}”這樣的參數(shù),要手工地做好過濾工作,來防止sql注入攻擊。

  6、在MyBatis中,“${xxx}”這樣格式的參數(shù)會直接參與SQL編譯,從而不能避免注入攻擊。但涉及到動態(tài)表名和列名時,只能使用“${xxx}”這樣的參數(shù)格式。所以,這樣的參數(shù)需要我們在代碼中手工進行處理來防止注入。

【結(jié)論】在編寫MyBatis的映射語句時,盡量采用“#{xxx}”這樣的格式。若不得不使用“${xxx}”這樣的參數(shù),要手工地做好過濾工作,來防止SQL注入攻擊。

mybatis是如何做到防止SQL注入的

? MyBatis框架作為一款半自動化的持久層框架,其SQL語句都要我們自己手動編寫,這個時候當(dāng)然需要防止SQL注入。其實,MyBatis的SQL是一個具有“輸入+輸出”的功能,類似于函數(shù)的結(jié)構(gòu),參考上面的兩個例子。其中,parameterType表示了輸入的參數(shù)類型,resultType表示了輸出的參數(shù)類型?;貞?yīng)上文,如果我們想防止SQL注入,理所當(dāng)然地要在輸入?yún)?shù)上下功夫。上面代碼中使用#的即輸入?yún)?shù)在SQL中拼接的部分,傳入?yún)?shù)后,打印出執(zhí)行的SQL語句,會看到SQL是這樣的:

select id, username, password, role from user where username=? and password=?

不管輸入什么參數(shù),打印出的SQL都是這樣的。這是因為MyBatis啟用了預(yù)編譯功能,在SQL執(zhí)行前,會先將上面的SQL發(fā)送給數(shù)據(jù)庫進行編譯;執(zhí)行時,直接使用編譯好的SQL,替換占位符“?”就可以了。因為SQL注入只能對編譯過程起作用,所以這樣的方式就很好地避免了SQL注入的問題。

【底層實現(xiàn)原理】MyBatis是如何做到SQL預(yù)編譯的呢?其實在框架底層,是JDBC中的PreparedStatement類在起作用,PreparedStatement是我們很熟悉的Statement的子類,它的對象包含了編譯好的SQL語句。這種“準(zhǔn)備好”的方式不僅能提高安全性,而且在多次執(zhí)行同一個SQL時,能夠提高效率。原因是SQL已編譯好,再次執(zhí)行時無需再編譯




SQL注入詳解的評論 (共 條)

分享到微博請遵守國家法律
英超| 乐昌市| 玛曲县| 三穗县| 惠来县| 石泉县| 安塞县| 莱州市| 石棉县| 正阳县| 陆丰市| 颍上县| 锡林郭勒盟| 凌源市| 濮阳县| 九江县| 顺义区| 宁晋县| 太仆寺旗| 黔江区| 吴江市| 徐州市| 呼伦贝尔市| 湖南省| 延吉市| 黑河市| 泸定县| 阿拉善左旗| 静海县| 房产| 定州市| 西贡区| 巫山县| 田东县| 神农架林区| 东城区| 丽水市| 唐海县| 江北区| 万宁市| 泾阳县|