宽字节注入及数据库编码分析
mysql的编码
在mysql中有几种常见编码:
1 | mysql> show variables like 'character%'; |
在默认情况下, mysql字符集为latin1, 而执行了set names utf8;
以后, character_set_client
, character_set_connection
, character_set_results
等与客户端相关的配置字符集都变成utf8, 但character_set_database
, character_set_server
等服务端相关的字符集还是latin1, 因为这一条语句, 导致客户端和服务端的字符集出现了差别, 既然有差别, mysql在执行查询的时候, 就设计到字符集的转换。
我们用php连接mysql服务器的时候设置的编码set names gbk
, 就等同于:
1 | set character_set_client='gbk' //客户端编码 |
连接顺序为: 客户端 <= 连接器 <= 服务端
, 这中间如果有哪一个编码不一致就很容易导致乱码的问题,
2008年鸟哥曾在博客中讲解了Mysql字符集:(http://www.laruence.com/2008/01/05/12.html)
MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection;
进行内部操作前将请求数据从character_set_connection转换为内部操作字符集
宽字节注入
在使用php连接mysql服务器的时候, 当设置 "set character_set_client=gbk"
(设置其他两个不会触发sql注入) 这时就会导致一个编码转换的注入问题, 也就是我们熟悉的宽字节注入
我们来分析下, 当我们提交id=1%df’
的时候,假设后端用了addslashes函数处理sql语句变成了select * from articles where id=1%df\'
如果php 连接mysql的时候设置了client客户端的字符集为gbk, 那么mysql服务器对查询语句就会进行GBK转码, mysql服务器数据库一般都是latin1编码,而gbk是两个字符表示一个汉字,因此,`%df 和转义字符%5c就会组成成一个%df%5c的汉字, 吃掉了一个转义符,导致单引号逃逸
解决方法:
- 一般官方推荐把charackter_set_client 设置为binary 或utf-8
- 使用mysql_real_escape_string()函数, 该函数会自动识别编码问题
另外在高版本的mysql中好像修复了该问题
范围: 据gbk编码,第一个字节ascii码大于128, 即0x81~0xff
mysql的其他编码问题
mysql的编码问题还会导致如下问题
1 | <?php |
核心代码如上
那么,为什么执行SELECT * FROM user WHERE username='admin\xC2' and password='admin'
却可以查出用户名是admin的记录?
当设置客户端的字符集位utf8的时候,这时mysql的编码转换为:
utf8-utf8-latin1
最后执行将数据库里的admin
和传入的admin\xC2
进行比较的时候, admin
是一个latin1字符串
猜测原因是Mysql在转换字符集的时候, 将不完整的字符给忽略了