什么是宽字节
当某字符的大小为一个字节时,称其字符为窄字节
当某字符的大小为两个字节时,称其字符为宽字节
所有英文默认占一个字节,汉字占两个字节
常见的宽字节编码:GB2312,GBK,GB18030,BIG5,Shift_JIS等等
使用宽字节注入的前提
数据库使用了gbk编码
使用了过滤函数,将用户输入的单引号转义(mysql_real_escape_string,addslashes)
这样被处理后的sql语句中,单引号不再具有‘作用’,仅仅是‘内容’而已,换句话说,这个单引号无法发挥和前后单引号闭合的作用,仅仅成为‘内容’,具体如下:
select * from users where id = ' 1 ' ;若注入语句为:1' order by 2#通过过滤函数拼接到sql语句中变为:1\' order by 2#即:select * from users where id = ' 1\' order by 2#;使'失效
什么是宽字节注入
绕过对于用户输入单引号等特殊符号的转义处理,使转义符号与输入的字符结合形成一个新的字符,从而使单引号逃脱转义处理,进行注入。
要绕过这个转义处理,使单引号发挥作用,有两个思路:
- 让斜杠(\)失去作用:对斜杠(\)转义,使其失去转义单引号的作用,成为‘内容’
- 让斜杠(\)消失:宽字节注入

当使用宽字节编码,如:GBK时,两个连在一起的字符会被认为是汉字,我们可以在单引号前加一个字符,使其和斜杠(\)组合被认为成汉字,从未达到让斜杠消失的目的,进而使单引号发挥作用.
注意:前一个字符的Ascii要大于128,两个字符才能组合成汉字
例如:129=0x81,加%即可,即%81’
注入案例(来自pikachu的SQL宽字节注入)
通过%df与\组成汉字来使单引号逃逸,获取数据库名字
获取表名

获取数据库中第二张表的字段名
注意:本来这里应该是table_name=‘users’,但因为对单引号进行了转义处理,所以不能使用’user’
但是可以通过嵌套一个select来查询到表名

获取字段值

防御
- 先调用mysql_set_charset函数设置连接所使用的字符集为gbk,再调用mysql_real_escape_string来过滤用户输入
- 将character_set_client设置为binary(二进制)
SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary
这几个变量是什么意思?
当我们的mysql接受到客户端的数据后,会认为他的编码是character_set_client,然后会将之将换成character_set_connection的编码,然后进入具体表和字段后,再转换成字段对应的编码。然后,当查询结果产生后,会从表和字段的编码,转换成character_set_results编码,返回给客户端。 所以,我们将character_set_client设置成binary,就不存在宽字节或多字节的问题了,所有数据以二进制的形式传递,就能有效避免宽字符注入。

