第7章 注入攻擊
- 注入攻擊是Web安全領域中一種最爲常見的攻擊方式。
- 注入攻擊的本質,是把用戶輸入的數據當做代碼執行。
- 有兩個關鍵條件:
- 用戶能夠控制輸入
- 原本程序要執行的代碼,拼接了用戶的數據
7.1 SQL注入
- SQL注入的典型例子
- “拼接”的過程很重要,正是這個拼接的過程,導致了代碼的注入。
- Web服務器開啓了錯誤回顯,會爲攻擊者提供極大的便利。
- 錯誤回顯披露了敏感信息,對於攻擊者來說構造SQL注入的語句就可以更加得心應手。
7.1.1 盲注(Blind Injection)
- 盲注,就是在服務器沒有錯誤回顯時完成的注入攻擊。
- 最常見的盲注驗證方法是,構造簡單的條件語句,根據返回頁面是否變化,來判斷SQL語句是否得到執行。
- 爲了進一步確認注入是否存在,攻擊者還必須再次驗證則個過程。
- 因爲一些處理邏輯或安全功能,在攻擊者構造異常請求時,也可能會導致頁面返回不正常。
- 服務器雖然關閉了錯誤回顯,但是攻擊者通過簡單的條件判斷,再對比頁面返回結果的差異,就可以判斷出SQL注入漏洞是否存在。
- 以上就是盲注的工作原理。
- 爲了進一步確認注入是否存在,攻擊者還必須再次驗證則個過程。
- 舉例:
and 1=1
返回正常界面and 1=2
無法返回查詢到的數據- 由此可判斷漏洞存在
7.1.2 Timing Attack
- MySQL一例;
- 利用BENCHMARK()函數,可以讓同一個函數執行若干次,使得結果返回的時間比平時要長;通過時間長短的變化,可以判斷出注入語句是否執行成功。
- 在不同的數據庫中,都有着類似於BENCHMARK()的函數,可以被Timing Attack所利用。
7.2 數據庫攻擊技巧
SQL注入是基於數據庫的一種攻擊。不同的數據庫有着不同的功能、不同的語法和函數。因此針對不同的數據庫,SQL注入的技巧也有所不同。
7.2.1 常見的攻擊技巧
- 猜解出數據庫的對應版本
- 利用union select來分別確認表名admin是否存在,列名passwd是否存在
- 想要猜解出username和passwd具體的值,可以通過判斷字符的範圍,一步步讀出來。
- 在注入攻擊的過程中,常常會用到一些讀寫文件的技巧。
- 寫入文件的技巧,經常被用於導出一個Webshell,爲攻擊者的進一步攻擊做鋪墊。
7.2.2 命令執行
- 在MySQL中,除了可以通過導出Webshell間接地執行命令外,還可以利用“用戶自定義函數”的技巧,即UDF(User-Defined Function)來執行命令。
- 例子
- 例1:在MySQL 4的服務器上,通過UDF執行系統命令。
- 例2:通過lib_mysqludf_sys提供的幾個函數執行系統命令,其中最主要的函數是sys_eval()和sys_exec()。
- 自動化注入工具sqlmap已經集成了此功能。
- 例3:在MS SQL Server中,可以直接使用存儲過程“xp_cmdshelll”執行系統命令
- 例4:在Oracle數據庫中,如果服務器同時還有Java環境,當SQL注入後可以執行多語句的情況下,可以在Oracle中創建Java的存儲過程執行系統命令。
- 一般來說,在數據庫中執行系統命令,要求具有較高的權限。在數據庫加固時,可以參考官方文檔給出的安全指導文檔。
- 在建立數據庫賬戶時應該遵循“最小權限則”,應該避免給Web應用使用數據庫的管理員權限。
7.2.3 攻擊存儲過程
- 存儲過程與UDF很像。但存儲過程必須使用CALL或者EXECUTE來執行。在MS SQL Server和Oracle數據庫中,都有大量的存儲過程。在注入攻擊中,存儲過程將爲攻擊者提供很大的便利。
- 舉例:MS SQL Server中的存儲過程“xp_cmdshell”
- 其他可以操作註冊表的存儲過程
- 其他可以攻擊的存儲過程
- 除了利用存儲過程直接攻擊外,存儲過程本身也可能會存在注入漏洞。
7.2.4 編碼問題
- 基於字符集的注入攻擊技巧
- 注入攻擊中常常會用到單引號、雙引號等特殊字符。在應用中,開發者爲了安全,經常使用轉義字符“\”來轉義這些特殊字符。但當數據庫使用了“寬字符集”時,可能會產生一些意想不到的漏洞。
- 要解決這種問題,需要統一數據庫、操作系統、Web應用所使用的字符集,以避免各層對字符的理解存在差異。統一設置爲UTF-8是一個很好的方法。
- 基於字符集的攻擊不侷限於SQL注入,凡是會解析數據的地方都可能存在此問題。
- XSS攻擊時,由於瀏覽器與服務器返回的字符編碼不同看,也可能會存在字符集攻擊。
- 解決方法:在HTML界面的
<meta>
標籤中指定當前頁面的charset。
- 如果因爲種種原因無法統一字符編碼,則需要單獨實現一個用於過濾或轉義的安全函數,在其中需要考慮到字符的可能範圍。
7.2.5 SQL Column Truncation
- MySQL的配置選項中,有一個sql_mode選項。
- 當其設置爲default時,即沒有開啓STRICT_ALL_TABLES選項時,MySQL對於用戶插入的超長值只會題是warning,而不是error(error則插入不成功),這可能會導致發生一些“截斷”問題。
- 舉例:
- 插入一個名稱爲admin[55 space chars]x的用戶到數據庫中,由於截斷,就會有第二個admin用戶存在於數據庫中。
7.3 正確地防禦SQL注入
- 從防禦的角度來看,要做的事有兩件:
- 找到所有的SQL注入漏洞
- 修補這些漏洞。
- 基於黑名單來過濾字符的方法,都或多或少存在一些問題。
7.3.1 使用預編譯語句
- 一般來說,防禦SQL注入的最佳方式,就是使用預編譯語句,綁定變量。
- 使用預編譯的SQL語句,SQL語句的語義不會發生改變。
- PHP中可以綁定變量
- 在不同的語言中,都有着使用預編譯語句的方法。
7.3.2 使用存儲過程
- 使用存儲過程的效果和使用預編譯語句類似,其區別就是存儲過程需要先將SQL語句定義在數據庫中。
- 注意,存儲過程中也可能會存在注入問題,因此應該儘量避免在存儲過程中使用動態的SQL語句。
- 如果無法避免,則應該使用嚴格的輸入過濾或者是編碼函數來處理用戶的輸入數據。
- 有時候,無法使用預編譯語句或存儲過程,該怎麼辦?
- 只能在此回到過濾和編碼等方法上來。
7.3.3 檢查數據類型
- 檢查輸入數據的數據類型,在很大程度上可以對抗SQL注入。
- 舉例
- 限制輸入數據的類型只能爲integer
- 輸入郵箱時,必須嚴格按照郵箱的格式
- 輸入時間、日期,必須嚴格按照時間、日期的格式
- 數據類型檢查並非萬能
- 比如,需要用戶提交字符串,如一段短文,則需要其他方法防範SQL注入
7.3.4 使用安全函數
- 我們需要一個足夠安全的編碼函數。數據廠商往往都對此做出了“指導”。
- 例如:
- MySQL中,按照書中舉例編碼字符
- 也可以參考OWASP ESAPI中的實現。
- 從數據庫自身的角度來說,應該使用最小權限原則,避免Web應用直接使用root、dbowner等高權限賬戶直接連接數據庫。
- 有多個不同的應用在使用同一個數據庫,應該爲每個應用分配不同的賬戶。
- Web應用使用的數據庫行乎,不應該有創建自定義函數、操作本地文件的權限。
7.4 其他注入攻擊
這些注入攻擊都有相同的特點,就是應用違背了“數據與代碼分離”的原則。
7.4.1 XML注入
- XML是一種常用的標記語言,通過標籤對數據進行結構化表示。
- XML與HTML都是SGML(Standard Generalized Markup Language,標準通用標記語言)。
- XML注入,也需要滿足注入攻擊的兩大條件:
- 用戶能控制數據的輸入
- 程序拼湊了數據
- 修補方案
- 與HTML注入類似
- 對用戶輸入數據中包含的“語言本身的保留字符”進行轉義即可
7.4.2 代碼注入
- 代碼注入與命令注入往往都是由一些不安全的函數或者方法引起的,其中的典型代表就是eval()。
- 存在代碼注入漏洞的地方,與“後門”沒有區別。
- 在Java中也可以實施代碼注入,比如利用Java的腳本引擎ScriptEngineManager
- JSP的動態include也能導致代碼注入。
- 嚴格來說,PHPO、JSP的動態include(文件包含漏洞)導致的代碼執行,都可以算是一種代碼注入。
- 代碼注入多見於腳本語言,有時候代碼注入可以造成命令注入(Command Injection)。
- 例如,PHP,C語言
- 對抗代碼注入、命令注入時
- 需要禁用eval()、system()等可以執行命令的函數。
- 如果一定要使用這些函數,則需要對用戶的輸入數據進行處理。
- 在PHP/JSP中避免動態include遠程文件,或者安全地處理它。
- 代碼注入往往是由於不安全的編程習慣所造成的,危險函數應該儘量避免在開發中使用,可以在開發規範中明確指出哪些函數是禁止使用的。
7.4.3 CRLF注入
- CRLF
- CR,Carriage Return(ASCII 13, \r)
- LF,Line Feed(ASCIi 10, \n)
- \r\n用於表示換行,十六進制編碼如下:
- \r 0x0d
- \n 0x0a
- log注入
- 在日誌文件中僞造admin用戶的登錄失敗記錄
- 注入HTTP頭
- 又稱“HTTP Response Splitting”
- HTTP協議中,HTTP頭是通過“\r\n”來分隔的。
- 如果服務器端沒有過濾“\r\n”,而又把用戶輸入的數據放在HTPP頭中,則有可能導致安全隱患。
- 通過兩次CRLF注入到HTTP Body,完成XSS攻擊
- 注入一個HTTP頭,比如注入一個Link頭,關閉IE8的XSS Filter功能。
- 可以說它的危害甚至比XSS還要大,因爲它破壞了HTTP協議的完整性。
- 對抗CRLF的方法非常簡單,只需要處理好“\r”、“\n”這兩個保留字符即可,尤其是那些使用“換行符”作爲分隔符的完整性。
7.5 小結
- 注入攻擊是應用違背了“數據與代碼分離原則”導致的結果。
- 注入攻擊的兩個條件:
- 用戶能夠控制數據的輸入
- 代碼拼湊了用戶輸入的數據,把數據當作代碼執行了
- 對抗注入攻擊
- 牢記“數據與代碼分離原則”
- 在“拼湊”發生的地方進行安全檢查,就能避免此類問題