Web 技术研究所

我一直坚信着,Web 将会成为未来应用程序的主流

GBK字符集下的安全问题

  GBK中的汉字是双字节字符,低字节的范围从0x40开始。这其中就包含了我们经常用作转义字符的反斜杠“\”(0x5C),所以如果在高位上添加适当的字符就可以吞并反斜杠,组成一个GBK字符。而原本被反斜杠转义的字符就成为正常字符,从而带来一系列安全问题。
  这个问题10年前老外就发现了,但迄今为止还有很多网站依然存在这个漏洞。既然反斜杠会被其它字符滚并成GBK字符,那么在把这样一个字符串放入数据库查询时就会出现注入漏洞。
  现在我们有个这样处理登陆表单的PHP程序 <?
header('content-type: text/html; charset=gbk');
if($password=$_GET['password']){
  mysql_connect('localhost','root','******');
  mysql_select_db('test');
  mysql_set_charset('gbk');
  $r=mysql_query("
    select 1 from tab1
    where password='".mysql_escape_string($password)."'
  ") or die(mysql_error());
  echo '<script>onload=function(){';
  if(mysql_fetch_assoc($r))
    header('Location: http://www.web-tinker.com') xor die;
  else echo 'alert("密码错误");';
  echo '}</script>';
};
?>
<form>
  <input name="password" value="<?=htmlspecialchars($password)?>" />
  <input type="submit" value="提交" />
</form>
  假如我们不知道这个密码,甚至tab1这个表都是空的,我们如何才能登陆成功呢?当然是注入!用一个字符把反斜杠吞并掉就OK,只有低位是0x5C的任意字符都可以,我写了个小程序把它们都遍历了出来
  这其中也有一些不能用的,不过没事,挑一个喜欢的字吧~这里我用了\x81,用下面这个GET参数访问这个PHP程序就可以成功登陆 ?password=%81'+or+1%23   mysql_escape_string会把“'”转义成“\'”,而“\x81”和“\”在一起会合体,变成“乗”这个汉字。后面的单引号就被孤立了。于是SQL语句变成了 select 1 from tab1
where password='乗' or 1#'
  或上了一个1,所以条件永远为真,最后多余的单引号用#号注释掉。这样我们就可以不用密码登陆了。
  真正的问题不是出在GBK上,而是自己的程序没过滤好。我们提交给服务器的数据并不是一个有效的GBK字符串,而PHP中字符串的概念实际上是字节数组,所以它并不介意处理一个不规范的GBK字符串。我们在做转义之前还需要判断字符串是不是合法的编码,如果不是应该抛出错误才对!虽然这个问题可以简单的把“mysql_escape_string”函数替换成“mysql_real_escape_string”来解决,但是这么做只是把\x81也一起转义罢了,它依然不是个合法的GBK字符,虽然本次写入数据库可以正常工作,但无法保证从数据库读取之后在其它程序上也能正常工作。所以我认为正确的处理方式有以下两种:
    1. 清除非法字符后再入库
    2. 放弃本次操作,给客户端返回错误信息
  包含非法数据的都是一些攻击测试程序,正常的浏览器是不会提交非法字符的,所以我的程序通常使用第二种方式。不过现在使用GBK已经很少了,普遍都是UTF-8。UTF-8是安全的,其它所有低位可以包含\x5C的字符集都有同样的问题。
网名:
3.84.186.*
电子邮箱:
仅用于接收通知
提交 悄悄的告诉你,Ctrl+Enter 可以提交哦
神奇海螺
[查看全部主题]
各类Web技术问题讨论区
发起新主题
本模块采用即时聊天邮件通知的模式
让钛合金F5成为历史吧!
次碳酸钴的技术博客,文章原创,转载请保留原文链接 ^_^