寬字節注入和二次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=%27;然后urldecode()函數又進行了一次解碼,%27解碼成了’,於是最終URL變成了/test.php?id=1’,單引號引發了注入。rawurldecode()也會產生同樣的問題,因此這兩個函數需要慎用。
代碼演示:
1 <?php 2 header("Content-Type: text/html; charset=utf-8"); 3
4 $conn = mysql_connect('localhost', 'root', 'root'); 5 mysql_select_db("test", $conn); 6 //mysql_query("SET NAMES 'gbk'", $conn);
7
8 $id = mysql_real_escape_string($_GET['id']); 9 $id = urldecode($id); 10 $sql = "select * from test where id='$id'"; 11 $query = mysql_query($sql, $conn); 12 if($query == True) 13 { 14 $result = mysql_fetch_array($query); 15 $user = $result["user"]; 16 $email = $result["email"]; 17
18 print_r('用戶名: ' . $user . '<br />'); 19 print_r('郵 箱: ' . $email . '<br />'); 20 print_r('<br />SQL語句:' . $sql); 21 } 22
23 mysql_close($conn); 24 ?>
因為mysql_real_escape_string()是在urldecode()之前,所以並不能過濾由於urldecode()產生的單引號。
普通的注入會被轉義掉:
於是構造URL編碼引發注入:
ps:
$id = mysql_real_escape_string($_GET['id']); //這里進行了一次解碼,1%2527 --> 1%27
$id = urldecode($id); //這里進行二次解碼