曾经写过一篇文章介绍此问题,由于没有很好的例子,效果也不是很明显。今天又发现一些问题,拿出来跟大家说说。
早上看了下博客,有新文章更新,就读了下~发现代码高亮插件更新了,遂去编辑以前的文章,加上代码高亮。问题来了。
如图:
我发现了问题,便去高亮插件的博文下评论。不出意外,又碰到了问题。如图:
例子就举到这里。说下猜测吧。
- wp对用户输入的数据持不信任态度,进行了htmlspecialchars转义,存储到数据库中。输出再次htmlspecialchars转义。
- wp对用户输入的数据原样存储到了数据库中,输出的时候进行了两次htmlspecialchars转义。
原因猜的对不对也不重要了,重要的是如何适时并正确的转义它们。这句话的重点其实是在转义上。如果对转义的理解不够深刻,那么自然也就无法做到正确,更别提适时了。
那么什么是转义呢?有太多的地方都有转义的存在的。
大学的时候刚接触c语言,老师会告诉你\是转义字符,\n表示换行,\r表示回车而\t是制表符。在之后的教学中,如果想在c语言中表示一个\则需要写成两个\。因为字符串中的\是需要转义的。
html自然也是必不可少的技能,毕竟互联网的基础就是html啊。所以,我们知道了html代码,也知道了<标签>的使用方法。也曾经写过类似这样的代码
<a href=”#” title=”这里写1<2是绝对没有问题的”>test</a>
年少轻狂啊~其实html自身也有一套转义系统。如果想让用户最终看到<那我们就需要输出<给用户。这就是转义。html的转义。
后来我们接触到了mysql。写过这样的sql
insert into tbl (a, b, c) values(‘$a’, $b, $c)
我们信誓旦旦的认为,这个是没问题的。当有一天
$a=”‘”;//一个单引号
的时候,程序出错了。原来交给mysql执行的sql语句也需要转义。
敏而好学的我们自然不会放过正则表达式这个强有力的工具,便向它发起了挑战:我要匹配一个\。于是我们写了
preg_match(‘/\/’, $string);
发现连PHP这关都过不了。有警告说没发现成对的定界符。那应该是\把/转义了。接下来我们写成
preg_match(‘/\\/’, $string);
发现还是不对!同样的错误。然后,检查代码,php传递给preg_match函数的正则表达式确实是/\/了。这个是没有疑问的。(其实上一个写法也是同样的结果)那为什么preg_match函数会说没找到成对的定界符呢?猜测正则表达式中也同样存在转义的概念。并且跟php中字符串的转义概念还不太一致。遂尝试
preg_match(‘/\\\\/’, $string);
目的是用php表示一个字符串/\/给preg_match。如果正则表达式也存在转义,那么正则表达式看到这个字符串后,就会理解成/\/了。也就是我们要的结果了。果不其然,结果是可以正常匹配到我们要的\了。
说了这么多转义,其实我们大家都知道的。但是,为什么还是会出现最上边的那些错误的转义的出现呢?答案就是转义的时机不对!
举个常见的例子
- 用户提交数据
- php处理数据
- 存储到mysql中
- 用户再次访问,从mysql中取出数据
- 显示给用户
非常常见的做法,也是我在各个公司已经开源项目中看到的做法如下:
- 用户提交的数据,不能信任
- 先来个htmlspecialchars再说
- 进行必要的mysql_escape转义处理再写数据库
- 数据库中的数据是安全的,因为我之前处理过
- 直接输出数据库中的结果给用户
以上做法是导致最上边问题出现的根本原因:未在适当的时候进行正确的转义。注意两个词,适当、正确。
- 用户提交的数据,不能信任是正确的。
- 但是,不能信任的数据此时并不能对我们构成任何威胁。但是,要切记,此数据是脏数据,不干净的。
- 写数据库的时候,由于传递给mysql的sql是要符合mysql对sql的要求的,也就是mysql要能看懂,所以,可选用php提供的mysql_escape函数进行转义再提交给mysql。此时写入数据库中的数据是用户提交的原始数据。脏数据,不干净的。
- 数据库中的数据还是不干净的,直接输出给用户,是非常危险的。如果是html输出,那么进行htmlspecialchars转义是必须的。
- 用户看到了当时他输入的内容,无论是1<2还是<script>alert()</script>。
上边这个例子未提及富文本信息的处理,因为这个确实是复杂了一些。也是各大公司都没处理好的部分(乌云上哪家大公司的xss都有)。那么如何正确处理这部分内容呢?
直接一个htmlspecialchars那就不叫富文本了。那就黑名单吧!反正各大公司都是这么干的,黑客也是这么建议的。他们是不会错的。不久后,乌云上又有xss了,又被脱裤了。。。
黑名单不适用于安全,白名单更靠谱。针对富文本,我们可以建立白名单体系,强制用户的输入是我们期望的。比如:我们允许部分标签,标签内的部分属性,属性的值范围。
由于在网上没有找到相关的PHP内置函数或扩展,所以我用PHP实现了一个白名单的富文本处理类。代码见最下边。
上一个图吧
白名单处理富文本类见附件