宽字节注入
宽字节注入基础
GBK占用两字节,ASCII占用一字节,PHP中编码为GBK,函数执行添加的是ASCII编码,MySQL默认的字符集是GBK等宽字节字符集
在数据库中使用了宽字符集(GBK,GB2312等),除了英文都是一个字符占两字节;
MySQL在使用GBK编码的时候,会认为两个字符为一个汉字(ascii>128才能达到汉字范围);
在PHP中使用
addslashes
函数的时候,会对单引号%27进行转义,在前边加一个反斜杠”\”,变成%5c%27;可以在前边添加%df,形成%df%5c%27,而数据进入数据库中时前边的%df%5c两字节会被当成一个汉字;
%5c被吃掉了,单引号由此逃逸可以用来闭合语句。
程序员为了防止sql注入,对用户输入中的单引号(’)进行处理,在单引号前加上斜杠(\)进行转义,这样被处理后的sql语句中,单引号不再具有‘作用’,仅仅是‘内容’而已,换句话说,这个单引号无法发挥和前后单引号闭合的作用,仅仅成为‘内容‘
而安全测试人员要绕过这个转义处理,使单引号发挥作用,有两个思路:
-
让斜杠(\)失去作用(对反斜杠(\)转义,使其失去转义单引号的作用,成为‘内容’)
-
让斜杠(\)消失(宽字节注入)
修复建议
- 统一使用utf-8字符集
utf-8字符集可以说是非常安全的字符集了,但是并不代表100%安全。
使用PHP函数iconv('utf-8','gbk',$_GET['id'])
,也可能导致注入产生iconv导致的致命后果
- 使用mysqli_set_charset(GBK)指定字符集
- 使用mysqli_real_escape_string(connection,escapestring)进行转义
- 在mysqli_query中设置参数,
$result=mysqli_query("character_set_client=binary", $sql);
至于为什么设置character_set_client=binary
就能防止宽字节注入,百度了很多资料,都是一笔带过模糊不清。在知乎里的一个回答,我是比较赞同的,但是他也并不清楚原因。
MySQL在传输时确定语义的一层就是character_set_client与后面的无关。
根据我的测试可以肯定的是如果设置character_set_client=binary
在这一层的编码上语义肯定发生了一些改变导致后面的恶意语句变成了无效编码从而被MySQL截断了,至于具体改变了什么就无从得知了。
挖个坑以后有机会再来填了
其实学到这里已经感觉到了SQL注入是什么一种感觉了这话说的有点虚无缥缈纳。