在瞭解寬字節注入以前,咱們先來看一看字符集是什麼。字符集也叫字符編碼,是一種將符號轉換爲二進制數的映射關係。
幾種常見的字符集:php
ASCII
編碼:單字節編碼latin1
編碼:單字節編碼gbk
編碼:使用一字節和雙字節編碼,0x00-0x7F
範圍內是一位,和 ASCII 保持一致。雙字節的第一字節範圍是0x81-0xFE
UTF-8
編碼:使用一至四字節編碼,0x00–0x7F
範圍內是一位,和 ASCII 保持一致。其它字符用二至四個字節變長表示。寬字節就是兩個以上的字節,寬字節注入產生的緣由就是各類字符編碼的不當操做,使得攻擊者能夠經過寬字節編碼繞過SQL注入防護。mysql
數據提交到MySQL數據庫,須要進行字符集的轉換,使得MySQL數據庫能夠對數據進行處理,這一過程通常有如下三個步驟:sql
character_set_client
->character_set_connection
。character_set_connection
-> 表建立的字符集
。表建立的字符集
-> character_set_results
。當執行set names "charset"
,至關於執行
set character_set_client = charset
set character_set_connection = charset
set character_set_results = charset
數據庫
client 指的是PHP程序
connection 指的是PHP客戶端與MySQL服務器之間鏈接層
results 指的是MySQL服務器返回給PHP客戶端的結果瀏覽器
MySQL常見的亂碼問題就是這三個字符集的設置不當所引發的。bash
這裏的演示環境爲 sqli-labs\Less-32服務器
<?php //including the Mysql connect parameters. include("../sql-connections/sql-connect.php"); function check_addslashes($string) { $string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string); //escape any backslash $string = preg_replace('/\'/i', '\\\'', $string); //escape single quote with a backslash $string = preg_replace('/\"/', "\\\"", $string); //escape double quote with a backslash return $string; } // take the variables if(isset($_GET['id'])) { $id=check_addslashes($_GET['id']); //echo "The filtered request is :" .$id . "<br>"; //logging the connection parameters to a file for analysis. $fp=fopen('result.txt','a'); fwrite($fp,'ID:'.$id."\n"); fclose($fp); // connectivity mysql_query("SET NAMES gbk"); $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; $result=mysql_query($sql); $row = mysql_fetch_array($result); if($row) { echo '<font color= "#00FF00">'; echo 'Your Login name:'. $row['username']; echo "<br>"; echo 'Your Password:' .$row['password']; echo "</font>"; } else { echo '<font color= "#FFFF00">'; print_r(mysql_error()); echo "</font>"; } } else { echo "Please input the ID as parameter with numeric value";} ?>
當用戶提交
http://127.0.0.1/Less-32/?id=1'
此時,發生以下轉換
第一步:'
被 check_addslashes
函數轉義爲 \'
函數
第二步:在執行sql查詢以前,也即\'
代入MySQL服務器以前,mysql_query("SET NAMES gbk");
將MySQL的三個字符集設置爲 gbk 編碼post
第三步:character_set_client
告訴MySQL Server
,傳入的是gbk編碼,也就是\'
被當作了%5C%27
傳入測試
第四步:character_set_client
-> character_set_connection
編碼徹底一致,數據沒有作任何轉換,因此輸入是%5C%27
,輸出的是%5C%27
第五步:character_set_connection
-> table charset
這裏咱們須要關注下所使用的表的字符集
能夠看到id
參數沒有設置編碼方式,不會對%5C%27
進行處理。在這裏MySQL服務器將查詢語句執行,並返回結果。
執行的SQL語句爲:
$sql="SELECT * FROM users WHERE id='1\'' LIMIT 0,1";
'
被轉義沒法進行注入
第六步:table charset
-> character_set_results
因爲character_set_results
字符集也設定爲 gbk ,保證了輸出內容沒有亂碼。
經過上面的分析,咱們發現用戶提交的數據實際上只被處理了兩次,一次是check_addslashes
對危險字符的處理,另外一次是gbk
編碼。因此,咱們能夠將以上步驟簡化爲:
數據 ==== (check_addslashes)====XXX====(GBK)====代入數據庫執行的內容
提交:
http://127.0.0.1/Less-32/?id=1%df'
('
瀏覽器自動進行url編碼%27
)
根據以上分析,發生以下轉換:
%df%27
====>(check_addslashes)====>%df%5c%27
====>(GBK)====>運'
MySQL執行的語句爲:
$sql="SELECT * FROM users WHERE id='1運'' LIMIT 0,1";
成功將單引號閉合,能夠進行SQL注入。
爲了不漏洞,網站通常會設置UTF-8編碼,而後進行轉義過濾。可是因爲一些不經意的字符集轉換,又會致使漏洞。
使用set names UTF-8
指定了UTF-8字符集,而且也使用轉義函數進行轉義。有時候,爲了不亂碼,會將一些用戶提交的GBK字符使用iconv
函數先轉爲UTF-8,而後再拼接入SQL語句。
mysql_query(「set names UTF-8」) ; $bar =iconv(「GBK」,」UTF-8」, addslashes($_GET[‘’bar])) ;
提交:
http://127.0.0.1/Less-32/?id=1%e5%5c%27
轉換:(%e5%5c
轉爲UTF-8爲e9%8c%a6
)
%e5%5c%27
====>(addslashes)====>%e5%5c%5c%5c%27
====(iconv)====>%e9%8c%a6%5c%5c%27
能夠看到,多出了一個%5c
,將轉義符(反斜槓)自己轉義,使得後面的%27
單引號發揮了做用
對於寬字節編碼,有一種最好的修補就是:
(1)使用mysql_set_charset(GBK)
指定字符集
(2)使用mysql_real_escape_string
進行轉義
原理是,mysql_real_escape_string
與addslashes
的不一樣之處在於其會考慮當前設置的字符集,不會出現前面e5和5c拼接爲一個寬字節的問題,可是這個「當前字符集」如何肯定呢?
就是使用mysql_set_charset
進行指定。
上述的兩個條件是「與」運算的關係,少一條都不行。
<meta charset="utf-8">
測試;
輸出: