不是 XSS 漏洞的 UTF-8 字符

UTF-8 characters that aren't XSS vulnerabilities

提问人:Jeff Day 提问时间:4/30/2009 最后编辑:Alan MooreJeff Day 更新时间:5/19/2009 访问量:4321

问:

我正在研究对字符串进行编码以防止 XSS 攻击。现在我们想使用白名单方法,其中白名单之外的任何字符都将被编码。 现在,我们采用“(”之类的东西并输出“(”。据我们所知,这将阻止大多数 XSS。

问题是我们有很多国际用户,当整个网站都是日语时,编码就成了主要的带宽消耗。是否可以肯定地说,基本 ASCII 集之外的任何字符都不是漏洞并且不需要编码,或者 ASCII 集之外的字符是否仍需要编码?

PHP UTF-8 XSS

评论


答:

11赞 VolkerK 4/30/2009 #1

如果您只是将编码传递给 htmlentities()/htmlspecialchars,可能会(很多)更容易

echo htmlspecialchars($string,  ENT_QUOTES, 'utf-8');

但是,这是否足够取决于您打印的内容(以及打印位置)。

参见:
http://shiflett.org/blog/2005/dec/googles-xss-vulnerability
http://jimbojw.com/wiki/index.php?title=Sanitizing_user_input_against_XSS
http://www.erich-kachel.de/?p=415(德语。如果我在英语中发现类似的东西 -> 更新) 编辑:好吧,我想你可以在不流利的德语;)的情况下理解要点 字符串

javascript:eval(String.fromCharCode(97,108,101,114,116,40,39,88,83,83,39,41)) 
传递 htmlentities() 不变。现在考虑类似的东西这将发送到浏览器。这归结为虽然 htmlentities() 可以完成元素内容的工作,但对属性来说却不是那么好。
<a href="<?php echo htmlentities($_GET['homepage']); ?>"
<a href="javascript:eval(String.fromCharCode(97,108,101,114,116,40,39,88,83,83,39,41))">
href="javascript:eval(\"alert('XSS')\")"

评论

0赞 baptx 7/20/2019
从 PHP 5.6 开始,似乎不需要在 / 中设置编码:“尽管此参数在技术上是可选的,但如果您使用的是 PHP 5.5 或更早版本,或者如果您的 default_charset 配置选项可能为给定输入设置不正确,强烈建议您为代码指定正确的值。php.net/manual/en/function.htmlspecialchars.phphtmlentitieshtmlspecialchars
5赞 Daniel Martin 5/19/2009 #2

一般来说,是的,你可以依靠任何非 ASCII 的东西来保证“安全”,但是有一些非常重要的注意事项需要考虑:

  1. 始终确保你是什么 发送到客户端被标记为 UTF-8 格式。这意味着有一个标头 明确表示“Content-Type: 文本/html;charset=utf-8“ 在每个 单个页面,包括您的所有页面 错误页面(如果有任何内容) 这些错误页面是从 用户输入。(很多人忘记了 测试他们的 404 页面,并拥有它 页面逐字包含未找到的 URL)
  2. 始终确保 您发送给客户的内容是 有效的 UTF-8。这意味着你不能简单地通过 从用户接收回的字节数 再次使用用户。你需要解码 字节为 UTF-8,应用 html 编码 XSS 防护,然后编码 当您将它们写回时,它们作为 UTF-8 外。

这两个警告中的第一个是防止客户端的浏览器看到一堆东西,包括高字母字符,并回退到一些本地多字节字符集。该本地多字节字符集可能有多种方法来指定您无法防御的有害 ASCII 字符。与此相关的是,某些浏览器的一些旧版本 - 咳 - 在检测页面是 UTF-7 时有点过于急切;这开启了无穷无尽的 XSS 可能性。为了防止这种情况,您可能需要确保对任何传出的“+”号进行 html 编码;当您生成适当的 Content-Type 标头时,这是过度的偏执狂,但当将来有人拨动关闭自定义标头的开关时,这将拯救您。(例如,通过在应用前面放置一个配置不佳的缓存反向代理,或者通过执行某些操作来插入额外的横幅标题 - 如果已经写入了任何输出,php 将不允许您设置任何 HTTP 标头)

第二个原因是,在 UTF-8 中可以指定“过短”的序列,这些序列虽然在当前规范下无效,但会被较旧的浏览器解释为 ASCII 字符。(看看维基百科怎么说)此外,有人可能会在请求中插入一个坏字节;如果将此包传递给用户,则可能会导致某些浏览器将坏字节及其后面的一个或多个字节替换为“?”或其他一些“无法理解此”字符。也就是说,一个坏字节可能会导致一些好的字节也被吞噬。如果你仔细观察你输出的内容,可能会在某个地方,一个能够从输出中擦除一两个字节的攻击者可以做一些XSS。将输入解码为 UTF-8,然后对其进行重新编码可防止此攻击媒介。