首先做个实验,在本地环境中建立这样一个PHP文件
<?php
header("Content-Type:text/html;Charset=gb2312");
echo _GET["str"];echi "<br/>";
echo addslashes(_GET["str"]);
?> 这里我的PHP环境已经打开了Magic_quotes_gpc,同时代码里面也作了对GET方法信息的转移,通常情况下
会对’这样的敏感字符进行处理,看起来万无一失.
不过,如果...嘿嘿,
发现问题了么?我们输入的是一个%d5’,而PHP却并没有将’进行转义!因此产生了一个
注入点!!!那么问题聚焦在%d5这里,这到底是怎么回事呢,原来PHP转移的底层函数php_escape_shell_cmd存在一个漏洞,
php通过将",’,#,&,;.....等等在shell命令行里有特殊意义的字符都通过在前面加上\变成\".\’,\#,\&, \;......来进行转义,使得用户的输入被过滤,来避免产生command injection漏洞。在php看来,只要过滤了这些字符,送入到system等函数中时,参数就会是安全的.
但实际上,对于GBK(GB231可以认为是其子集,GBK扩展了对繁体中文的支持等)编码,一个中文字符被处理成两个字节保存:
首字节范围在0x81-0xFE
尾字节范围在0x40-0xFE(不含0x7F)
请注意,
对于转义用到的"\"(0x5C)恰恰包含在尾字节当中!当我们提交一个0x81-0xFE的字节(本例是0xD5)并故意带上一个敏感字符" ’ "时,PHP针对" ’ "进行转义,产生一个" \ "(0x5c),恰好与前面的0xd5结合在一起成为一个完整的字符"誠",原本的转义符号"反水"了同样的,这个漏洞对于POST,Cookie都有效.个人以为较好的解决办法就是在增加单独的处理方法,从根本上去处理这些非法字符.
谈到这里,一定有人回想我常用的是UTF-8编码,存不存在这个问题呢?
值得庆幸的是,Unicode编码不存在这个问题,不想看原因的朋友大可以略过到后文:)
UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
下表总结了编码规则,字母x表示可用编码的位。
Unicode符号范围 | UTF-8编码方式
(十六进制) | (二进制)
--------------------+---------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
有兴趣的朋友去查汉字unicode编码表,再结合上表就会发现汉字几乎都需要3个字节来处理,而三个字节最小值也得是1000 0000 即0x80," \ "不在其中.
似乎用UTF-8的朋友可以安心了,但是事实上PHP针对unicode转码却也存在一个漏洞.不过利用起来极难.有兴趣的可以自行查找XD