宽字符注入详解
之前写过一篇宽字符注入的(https://liotree.github.io/2019/05/06/%E5%AE%BD%E5%AD%97%E7%AC%A6%E6%B3%A8%E5%85%A5/)
不过有点太简略了,新写一篇详细的
1.mysql的编码
先来了解一些mysql的知识
mysql 有以下几个字符集的系统变量:
- character_set_server 内部操作字符集
- character_set_client 客户端使用的字符集
- character_set_connection 连接层字符集
- character_set_results:查询结果字符集
- character_set_database:当前选中数据库的默认字符集
- character_set_system:系统元数据(字段名等)字符集
当php操作mysql时,mysql会认为php传过来的数据是character_set_client编码的,接着将数据转为character_set_connection的形式,最后再转为内部操作字符集
内部操作字符集按如下方式确定(引用自https://blog.csdn.net/zy691357966/article/details/79802587):
使用每个数据字段的CHARACTER SET设定值;
若上述值不存在,则使用对应数据表的DEFAULT CHARACTER SET设定值(MySQL扩展,非SQL标准);
若上述值不存在,则使用对应数据库的DEFAULT CHARACTER SET设定值;
若上述值不存在,则使用character_set_server设定值。
最后将操作结果从内部操作字符集转换为chatacter_set_results
2.宽字符注入原理
都是gbk编码的情况
代码示例:
1 | $id = addslashes($_GET['id']); |
当使用了
1 | mysql_query("set names gbk"); |
时,character_set_client,character_set_connection和character_set_result都会被设置成gbk
因此就可以利用gbk编码去吃掉,详情可以参见我之前的文章
https://liotree.github.io/2019/05/06/宽字符注入/
使用了转换编码格式的函数
php中转换编码格式的函数有iconv和mb_convert_encoding
a.从gbk转为utf-8
示例代码
1 | mysql_query("set names UTF-8") ; |
加入我们传入一个%e5%5c%27,按照gbk编码也就是
錦’
addslashes会给/(%5c)和’(%27)前面加上/,因此变为%e5%5c%5c%5c%27
%e5%5c%5c%5c%27 转换为utf-8后变为%e9%8c%a6%5c%5c%27
按照utf8即为
錦\\‘
也就是\被转义了,逃逸出了’
b.从utf-8转为gbk
示例代码
1 | mysql_query("set names UTF-8") ; |
给username参数传入一个%e9%8c%a6%27,也就是utf-8编码的
錦’
addslashes会将其变成%e9%8c%a6%5c%27,转为gbk编码则变成了
%e5%5c%5c%27,也就是gbk编码的
錦\\‘
这时\被转义了,’逃逸了出来
3.防御宽字符注入
示例代码:
1 | $username = $_GET['username']; |
使用mysql_set_charset(GBK)代替mysql_query(“set names gbk”);
mysql_set_charset除了会set name之外,还会将mysql->charset设置为指定的编码(详细参见http://www.laruence.com/2010/04/12/1396.html)
mysql_real_escape_string和addslashes以及mysql_escape_string的区别就是mysql_real_escape会按照mysql->charset指定的字符集来看待传入的字符串
举个例子
1 |
|
输出
string(5) “慭\\‘“ string(5) “慭\\‘“ string(5) “慭\\‘“ string(4) “慭\‘“
当然,更好的防御方法是使用mysqli或者pdo的参数化查询