本文介紹由于PHP的不安全編碼引起的各類SQL注入。經(jīng)常看到網(wǎng)站被拖庫(kù),造成信息泄露,主要就是SQL注入漏洞造成的。
1、什么是SQL注入
SQL注入漏洞為PHP研發(fā)人員所熟知,它是所有漏洞類型中危害最嚴(yán)重的漏洞之一。SQL注入漏洞,主要是通過(guò)偽造客戶端請(qǐng)求,把SQL命令提交到服務(wù)端進(jìn)行非法請(qǐng)求的操作,最終達(dá)到欺騙服務(wù)器從而執(zhí)行惡意的SQL命令。
研發(fā)人員在處理應(yīng)用程序和數(shù)據(jù)庫(kù)交互時(shí),未對(duì)用戶可控參數(shù)進(jìn)行嚴(yán)格的校驗(yàn)防范,例如使用字符串拼接的方式構(gòu)造SQL語(yǔ)句在數(shù)據(jù)庫(kù)中進(jìn)行執(zhí)行,很容易埋下安全隱患。
SQL注入可以造成數(shù)據(jù)庫(kù)信息泄露,特別是數(shù)據(jù)庫(kù)中存放的用戶隱私信息的泄露。通過(guò)操作數(shù)據(jù)庫(kù)對(duì)特定網(wǎng)頁(yè)進(jìn)行篡改,修改數(shù)據(jù)庫(kù)一些字段的值,嵌入惡意鏈接,進(jìn)行掛馬攻擊,傳播惡意軟件。服務(wù)器還容易被遠(yuǎn)程控制,被安裝后門,經(jīng)由數(shù)據(jù)庫(kù)服務(wù)器提供的操作系統(tǒng)支持,讓攻擊者得以修改或控制操作系統(tǒng)以及破壞硬盤數(shù)據(jù),癱瘓全系統(tǒng)。
目前常見的SQL注入的攻擊方式有報(bào)錯(cuò)注入、普通注入、隱式類型注入、盲注、寬字節(jié)注入、二次解碼注入。下面對(duì)每一種注入威脅舉例說(shuō)明,以幫助您在編碼過(guò)程中有效地避免漏洞的產(chǎn)生。
為了能更直觀地了解SQL注入,先在數(shù)據(jù)庫(kù)中創(chuàng)建一個(gè)名叫hacker的用戶表。下面是數(shù)據(jù)表的結(jié)構(gòu),示例都是通過(guò)這個(gè)表結(jié)構(gòu)來(lái)說(shuō)明的。
下面的一段PHP代碼,主要功能是在數(shù)據(jù)庫(kù)中通過(guò)用戶名查詢用戶的具體信息。通過(guò)這段代碼,來(lái)介紹SQL注入以及它對(duì)系統(tǒng)的危害。
2、報(bào)錯(cuò)注入
報(bào)錯(cuò)注入是指惡意攻擊者用特殊的方式使數(shù)據(jù)庫(kù)發(fā)生錯(cuò)誤并產(chǎn)生報(bào)錯(cuò)信息,從而獲得數(shù)據(jù)庫(kù)和系統(tǒng)信息,方便攻擊者進(jìn)行下一步攻擊。
需要注意,在研發(fā)過(guò)程中,如果傳入查詢參數(shù)且沒(méi)有對(duì)參數(shù)進(jìn)行嚴(yán)格處理,通常會(huì)造成SQL報(bào)錯(cuò)注入。
如果對(duì)$username傳入?yún)?shù)hacker'attack,完整請(qǐng)求http://localhost:8080/mysql.php?name=hacker'attack,查詢語(yǔ)句將變成以下形式。
這可以導(dǎo)致數(shù)據(jù)庫(kù)報(bào)錯(cuò),攻擊者就可以通過(guò)這種方式獲取MySQL的各類信息,從而對(duì)系統(tǒng)進(jìn)行下一步的攻擊和破壞。
為了防止報(bào)錯(cuò)信息被攻擊者直接看到,網(wǎng)站上線后需要設(shè)置display_errors=Off。
3、普通注入
下面的示例是普通注入。攻擊者在地址欄輸入下面帶有部分SQL語(yǔ)句的請(qǐng)求。
最終的SQL語(yǔ)句變成如下形式。
從而輸入任何參數(shù)都可以滿足查詢條件,使其變成一個(gè)萬(wàn)能查詢語(yǔ)句。同樣,可以使用UNION和多語(yǔ)句進(jìn)行查詢,獲取數(shù)據(jù)庫(kù)的全部信息。
完整請(qǐng)求URL:
數(shù)據(jù)庫(kù)當(dāng)前表中的數(shù)據(jù)將被全部備份在/tmp/backup.sql文件中。當(dāng)攻擊者再利用其他漏洞找到下載方式,將文件下載或者復(fù)制走,最終造成被拖庫(kù)時(shí),Web站點(diǎn)的數(shù)據(jù)就會(huì)全部暴露。
如果執(zhí)行下面請(qǐng)求,將發(fā)生更可怕的事情。
執(zhí)行上面的請(qǐng)求后,在原有的SQL語(yǔ)句后面拼接了name';DELETE FROM hacker;SELECT * FROM username WHERE 'a'='a,查詢語(yǔ)句變成了以下形式。
數(shù)據(jù)庫(kù)里的數(shù)據(jù)被攻擊者完全刪除。如果沒(méi)有提前對(duì)數(shù)據(jù)進(jìn)行備份,這對(duì)于系統(tǒng)造成的傷害將是毀滅性的。
4、隱式類型注入
以數(shù)據(jù)表結(jié)構(gòu)為例,編寫以下查詢語(yǔ)句。
該查詢語(yǔ)句的作用是通過(guò)email查詢相應(yīng)的用戶信息,由于將email的值誤寫為0,在圖1的執(zhí)行結(jié)果中可以看到數(shù)據(jù)庫(kù)返回了表中的所有內(nèi)容。
為什么會(huì)這樣呢?因?yàn)樵贛ySQL中執(zhí)行SQL查詢時(shí),如果SQL語(yǔ)句中字段的數(shù)據(jù)類型和對(duì)應(yīng)表中字段的數(shù)據(jù)類型不一致,MySQL查詢優(yōu)化器會(huì)將數(shù)據(jù)的類型進(jìn)行隱式轉(zhuǎn)換。表1中列出了SQL執(zhí)行過(guò)程中MySQL變量類型轉(zhuǎn)換規(guī)則,在研發(fā)過(guò)程中需要注意它的影響。
通過(guò)表中的轉(zhuǎn)換關(guān)系可以看出,在上面的查詢語(yǔ)句中,MySQL將數(shù)據(jù)類型轉(zhuǎn)換為DOUBLE后進(jìn)行查詢,由于STRING轉(zhuǎn)換后的值為0,同時(shí)查詢條件中的值也為0,所以匹配到了整張表的內(nèi)容。
5、盲注
報(bào)錯(cuò)注入和普通注入顯而易見,盲注有時(shí)容易被忽略。
在頁(yè)面無(wú)返回的情況下,攻擊者也可以通過(guò)延時(shí)等技術(shù)實(shí)現(xiàn)發(fā)現(xiàn)和利用注入漏洞。
判斷數(shù)據(jù)庫(kù)版本,執(zhí)行成功,瀏覽器返回會(huì)延時(shí)并利用BENCHMARK()函數(shù)進(jìn)行延時(shí)注入。
該請(qǐng)求會(huì)使MySQL的查詢睡眠5秒,攻擊者可以通過(guò)添加判斷條件到SQL語(yǔ)句中,如果睡眠了5秒,那么說(shuō)明MySQL版本為5,否則不是。通過(guò)盲注可以掌握數(shù)據(jù)庫(kù)和服務(wù)器的相關(guān)信息,為攻擊提供便利。
6、寬字節(jié)注入
要觸發(fā)寬字節(jié)注入,有一個(gè)前提條件,即數(shù)據(jù)庫(kù)和程序編碼都是GBK的。下面的示例代碼以GBK編碼格式保存。
在這個(gè)SQL語(yǔ)句前面,使用了一個(gè)addslashes()函數(shù),將$id的值進(jìn)行轉(zhuǎn)義處理。只要輸入?yún)?shù)中有單引號(hào),就逃逸不出限制,無(wú)法進(jìn)行SQL注入,具體如下。
上面兩個(gè)請(qǐng)求都通過(guò)了addslashes,不會(huì)引起SQL注入。要實(shí)現(xiàn)注入就要逃過(guò)addslashes的限制,addslashes()函數(shù)的作用是讓“'”變成”'”,進(jìn)行了轉(zhuǎn)義。攻擊者一般的繞過(guò)方式就是想辦法處理“'”前面的“\”。
PHP在使用GBK編碼的時(shí)候,會(huì)認(rèn)為兩個(gè)字符是一個(gè)漢字。當(dāng)輸入的第一個(gè)字符的ASCII碼大于128時(shí),看看會(huì)發(fā)生什么情況,例如輸入“%81'”。
MySQL報(bào)告出現(xiàn)語(yǔ)法SQL錯(cuò)誤,原因是多輸入了一個(gè)引號(hào),然而前面的反斜杠不見了,一旦出現(xiàn)數(shù)據(jù)庫(kù)報(bào)錯(cuò),就說(shuō)明可以進(jìn)行SQL注入了。
原因是GBK是多字節(jié)編碼,PHP認(rèn)為兩個(gè)字節(jié)代表一個(gè)漢字,所以%81和后面的反斜杠%5c變成了一個(gè)漢字“乗”,造成反斜杠消失。
7、二次解碼注入
通常情況下,為了防止SQL注入的發(fā)生,采取轉(zhuǎn)義特殊字符,例如對(duì)用戶輸入的單引號(hào)(')、雙引號(hào)(")進(jìn)行轉(zhuǎn)義變成“'"”。有一個(gè)誤區(qū)就是通過(guò)配置PHP的GPC開關(guān)進(jìn)行自動(dòng)轉(zhuǎn)義。
當(dāng)攻擊者將參數(shù)二次編碼時(shí),PHP的自動(dòng)轉(zhuǎn)義將無(wú)法識(shí)別用戶的惡意輸入。
用前面的URL,來(lái)構(gòu)造如下新的請(qǐng)求。
當(dāng)PHP接收到請(qǐng)求時(shí)會(huì)自動(dòng)進(jìn)行一次URL解碼,變?yōu)閚ame%27,然后代碼里又使用urldecode()函數(shù)或rawurldecode()函數(shù)進(jìn)行解碼,%27變成了單引號(hào),URL最終變成name=name'引發(fā)SQL注入。