在使用PHP和Mysql時,設定“setcharacter_set_client=gbk”時可能會導致寬位元組注入。寬位元組注入的原理是這樣子的,當提交1.php?id=1時,假如Mysql語句為“select * from user where id=’1’”,當參數使用了addslashes()、mysql_escape_string()或者GPC開關來過濾單引號(’)、雙引號(”)、反斜線(\)和NULL字元,會在這些符號前面加上反斜線加以轉義,如下:
<?phpheader("Content-Type: text/html; charset=utf-8");$conn = mysql_connect('localhost', 'root', 'root');mysql_select_db("test", $conn);mysql_query("SET NAMES GBK", $conn);//不安全的編碼方式$id = addslashes($_GET['id']);//不安全的轉義函數$sql = "select * from test where id='$id'";$query = mysql_query($sql, $conn);if($query == True){$result = mysql_fetch_array($query);$user = $result["user"];$email = $result["email"];print_r('使用者名稱: ' . $user . '<br />');print_r('郵 箱: ' . $email . '<br />');$sql = mb_convert_encoding($sql, "UTF-8", "GBK");print_r('<br />SQL語句:' . $sql);}mysql_close($conn);
單引號沒有閉合,無法實現注入。我們在參數後面加上%df%27,即可把程式中轉義的\(%5c)吃掉。這是因為%df和\組合成了%df%5c,解碼成了“運”字,後面的%27(單引號)沒有被轉義,成功實現了閉合。如下:
漏洞的產生是因為在PHP串連Mysql時執行了“setcharacter_set_client=gbk”,比如“SET NAMES GBK”,實際上是執行了如下操作:
set character_set_connection = gbk,
set character_set_results=gbk,
set character_set_client=gbk
而mysql_set_charset(‘gbk’)實際上是調用了SET NAMES,因此也存在漏洞。同樣的編碼轉換函式iconv()、mb_convert_encoding()等也會產生漏洞。
漏洞的防範方法有以下幾種:
1) 在執行查詢之前執行如下操作:SET NAMES GBK; character_set_client=binary。
2) 使用mysql_real_escape_string()安全轉義函數進行參數過濾。
3) 使用pdo方式先行編譯處理資料庫查詢。