SQL注入最易懂系列教程
在上節我們講了寬位元組注入,寬位元組注入主要是因為轉譯特殊字元時候,因為寬位元組佔用字元長度原因,構造語句從而消滅掉轉譯字元 ,從而導致寬位元組注入。二次編碼注入和寬位元組注入非常相似。
一.關於編碼的知識、
1.為什麼要進行Url編碼
我們都知道Http協議中參數的傳輸是"key=value"這種鍵值對形式的,如果要傳多個參數就需要用「&」符號對鍵值對進行分割。如"name1=value1&name2=value2",這樣在服務端在收到這種字元串的時候,會用「&」分割出每一個參數,然後再用「=」來分割出參數值。
現在有這樣一個問題,如果我的參數值中就包含=或&這種特殊字元的時候該怎麼辦。 比如說「name1=value1」,其中value1的值是「va&lu=e1」字元串,那麼實際在傳輸過程中就會變成這樣「name1=va&lu=e1」。我們的本意是就只有一個鍵值對,但是服務端會解析成兩個鍵值對,這樣就產生了奇異。
如何解決上述問題帶來的歧義呢?解決的辦法就是對參數進行URL編碼
URL編碼只是簡單的在特殊字元的各個位元組前加上%,例如,我們對上述會產生奇異的字元進行URL編碼後結果:「name1=va%26lu%3D」,這樣服務端會把緊跟在「%」後的位元組當成普通的位元組,就是不會把它當成各個參數或鍵值對的分隔符。
通常如果一樣東西需要編碼,說明這樣東西並不適合傳輸。原因多種多樣,如Size過大,包含隱私數據,對於Url來說,之所以要進行編碼,是因為Url中有些字元會引起歧義。
2.URL編碼
形式:「%」加上ASCII碼(先將字元轉換為兩位ASCII碼,再轉為16進位),其中加號「+」在URL編碼中和「%20」表示一樣,均為空格。
當遇到非ASCII碼錶示的字元時,如中文,瀏覽器或通過編寫URLEncode,根據UTF-8、GBK等編碼16進位形式,進行轉換。如「春」的UTF-8編碼為E6 98 A5,因此其在支持UTF-8的情況下,URL編碼為%E6%98%A5。值得注意的是採取不同的中文編碼,會有不同的URL編碼。
在URL傳遞到後台時,首先web容器會自動先對URL進行解析。容器解碼時,會根據設置(如jsp中,會使用request.setCharacterEncoding("UTF-8")),採用UTF-8或GBK等其中一種編碼進行解析。這時,程序無需自己再次解碼,便可以獲取參數(如使用request.getParameter(paramName))。
但是,有時從客戶端提交的URL無法確定是何種編碼,如果伺服器選擇的編碼方式不匹配,則會造成中文亂碼。為了解決這個問題,便出現了二次URLEncode的方法。在客戶端對URL進行兩次URLEncode,這樣類似上文提到的%E6%98%A5則會編碼為%25e6%2598%25a5,為純ASCII碼。Web容器在接到URL後,自動解析一次,因為不管容器使用何種編碼進行解析,都支持ASCII碼,不會出錯。然後在通過編寫程序對容器解析後的參數進行解碼,便可正確得到參數。在這裡,客戶端的第一次編碼,以及服務端的第二次解碼,均是由程序員自己設定的,是可控的,可知的。
二.urldecode()二次解碼引發注入
PHP中常用防注入過濾函數如addslashes()、mysql_real_escape_string()、mysql_escape_string()或者使用魔術引號GPC開關來防止注入,原理都是給單引號(』)、雙引號(」)、反斜杠()和NULL等特殊字元前面加上反斜杠來進行轉義。
但是這些函數在遇到urldecode()函數時,就會因為二次解碼引發注入。urldecode()函數是對已編碼的URL進行解碼。引發注入的原因其實很簡單,PHP本身在處理提交的數據之前會進行一次解碼,例如/test.php?id=1這個URL,我們構造字元串/test.php?id=1%2527,PHP第一次解碼,%25解碼成了%,於是url變成了/test.php?id=1%27;然後urldecode()函數又進行了一次解碼,%27解碼成了』,於是最終URL變成了/test.php?id=1』,單引號引發了注入。rawurldecode()也會產生同樣的問題,因此這兩個函數需要慎用
因為mysql_real_escape_string()是在urldecode()之前,所以並不能過濾由於urldecode()產生的單引號
舉個例子:
普通的注入會被轉譯掉:
於是構造url編碼引發注入
以後黑盒測試跑SQL注入可以在URL後面加上%2527,說不定就能碰到二次解碼引發注入的情況。


TAG:python黑客愛好者 |