前言在mysql中,用于转义的函数有addslashes、mysql_real_escape_string、mysql_escape_string等,还有magic_quote_gpc的情况,但是更高版本的PHP会去掉这个功能。首先,宽字节注入与HTML页面编码无关。我曾经看到并放弃了尝试。这是一种误解。SQL注入不是XSS。虽然它们中代码的成因相似,但发生的地方不同。网上很多资料都说程序使用宽字节来处理程序,但是并没有说明具体指的是什么程序。本文将介绍具体漏洞的原理和简单利用。这里我们限定使用的语言是PHP5.4,数据库MYSQL5.6。涉及的一些概念字符、字符集和字符序列字符是构成字符集的基本单位。给字符赋值(编码),以确定字符在字符集中的位置。字符顺序(collat??ion)是指同一字符集中字符之间的比较规则。UTF8用ASCII表示的字符只有128个,所以网络世界的规范是使用UNICODE编码,但是对ASCII表示的字符使用UNICODE效率不高。因此,出现了一种中间格式字符集,称为通用转换格式,UTF(UniversalTransformationFormat)。宽字节GB2312、GBK、GB18030、BIG5、Shift_JIS等常说是宽字节,其实只有两个字节。宽字节带来的安全问题主要是吃ASCII字符(一个字节)的现象。MYSQL字符集转换过程1、MySQLServer在收到请求时,将请求数据从character_set_client转换为character_set_connection;2.在执行内部操作之前,将请求数据从character_set_connection转换为内部操作字符集。确定方法如下:?使用数据字段的各个CHARACTERSET设置值;?如果上述值不存在,则使用对应数据表的DEFAULTCHARACTERSET设置值(MySQL扩展,非SQL标准);?如果上述值不存在,则使用对应数据库CHARACTERSET设置值的DEFAULT;?如果上述值不存在,则使用character_set_server设置值。将内部运算字符集的运算结果转换为character_set_results。重要:发生宽字节注入的地方是PHP向MYSQL发送请求时,使用character_set_client设置值对字符集进行一次编码。PHP测试代码:';如果($res=mysql_query($sql)){而($row=mysql_fetch_array($res)){var_dump($row);}}else{echo"Error".mysql_error()."
";}?>http://localhost/xl.php?sql=root%df%27%20or%201=1%23可以执行成功!URL解码sql=root?'or1=1#分析过程:$_GET['sql']被addslashes编码然后带入'\'1,root%df%5C%27%20or%201=1%232,and带入mysql处理使用gbk字符集%df%5c->%5c%27被成功吃掉->'单引号成功闭合,执行插入的sql语句吃法:GBK编码,其编码范围为0×8140~0xFEFE(不包括xx7F),%5c遇到%df(ascii(223))>ascii(128)会自动拼接,所以'\'会被吃掉,%27和%20小于的字符ascii(128)将被保留。补充:GB2312兼容GBK,其高位范围为0xA1~0xF7,低位范围为0xA1~0xFE(0x5C不在此范围内),所以不能用编码吃%5c。其他宽字符集的分析过程相同。要吃%5c,你只需要在低位包含正常的0x5c。安全过滤上面的代码使用了mysql_query("setnamesgbk")来设置编码。其实mysql_set_charset("gbk");推荐在mysql中使用function来设置编码。这两个函数的作用大致相似。***不同的是后者会修改mysql对象中的mysql->charset属性为设置的字符集。同时匹配的过滤函数是mysql_real_escape_string()。上面的代码中列出了几个过滤函数。它们的区别在于mysql_real_escape_string()会根据mysql对象中的mysql->charset属性来处理传入的字符串,因此可以根据当前的字符集进行过滤。具体区别可以参考:http://www.laruence.com/2010/04/12/1396.html同理,由上可知,由于转码形成了宽字节注入,具有以下功能转码的功能也是造成漏洞的原因。下面用iconv()演示转码函数mb_convert_encoding()iconv(),修改上面代码:';$res=mysql_query($sql);while($row=mysql_fetch_array($res)){var_dump($row);}?>http://localhost/xl.php?sql=root%e5%27or%201=1%23也可以执行成功,编码解析过程还是和上面一样。漏洞成因总结:代码11.使用了不安全字符集设置函数和过滤函数。2、PHP请求mysql时,使用character_set_client值转码一次,就会出现该漏洞。代码21.使用推荐的设置功能和过滤功能。2、iconv()函数转码时出现解析错误,GBK转UTF8吃“\”3、PHP请求mysql时转码是安全的。另外:改变编码方向时,$user=iconv('UTF-8','gbk',$user);可以通过访问http://localhost/xl.php?sql=root%e9%8c%a6A\来引入,这又会注释掉单引号。在这种情况下,需要两个参数来协调注入。例如:http://localhost/xl.php?sql=root%e9%8c%a6?=%20or%201=1%23总结:宽字节注入与HTML页面编码无关。mysql编码和过滤函数推荐使用mysql_real_escape_string()、mysql_set_charset()。转码功能也可能导致宽字节注入,即使使用安全设置功能也是如此。