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