安全的 XSS 清理功能(定期更新)

Posted

技术标签:

【中文标题】安全的 XSS 清理功能(定期更新)【英文标题】:Secure XSS cleaning function (updated regularly) 【发布时间】:2011-09-16 23:14:22 【问题描述】:

我已经在网上搜索了几天,试图弄清楚这一点,但得到的答案相互矛盾。

是否有针对 PHP 的库、类或函数可以针对 XSS 安全地清理/编码字符串?它需要定期更新以应对新的攻击。

我有几个用例:

用例 1) 我有一个纯文本字段,比如名字或姓氏

用户在字段中输入文本并提交表单 在将其保存到数据库之前,我想 a) 修剪前面的所有空白并 字符串的结尾,并且 b) 从输入中删除所有 html 标记。这是一个名称文本字段,其中不应包含任何 HTML。 然后我将使用 PDO 准备语句将其保存到数据库中。

我想我可以只做trim()strip_tags() 然后使用Sanitize Filter 或带有字符白名单的RegEx。他们真的需要像!和 ?或 < > 在他们的名字中,不是真的。

用例 2) 当将内容从以前保存的数据库记录(或以前提交的表单)输出到视图/HTML 时,我想彻底清理它以用于 XSS。 注意:在用例 1 中它可能已经或可能没有经过过滤步骤,因为它可能是不同类型的输入,因此假设没有进行任何清理。

最初我虽然 HTMLPurifier 可以完成这项工作,但似乎 不是I posed the question to their support 时我需要的:

这是试金石:如果用户提交 <b>foo</b>,它应该显示为 <b>foo</b> 还是 foo?如果是前者,则不需要 HTML Purifier。

所以我宁愿它显示为<b>foo</b>,因为我不希望为简单的文本字段显示任何 HTML 或执行任何 javascript

所以我一直在寻找可以为我完成这一切的功能。我偶然发现了xss_clean method used by Kohana 3.0,我猜它是可行的,但前提是你想保留 HTML。它现在已从 Kohana 3.1 中弃用,因为他们已将其替换为 HTMLPurifier。所以我猜你应该做HTML::chars()而不是this code:

public static function chars($value, $double_encode = TRUE)

    return htmlspecialchars( (string) $value, ENT_QUOTES, Kohana::$charset, $double_encode);

现在显然你应该使用 htmlentities 而不是提到的 in quite a few places in Stack Overflow,因为它比 htmlspecialchars 更安全。

那么如何使用 htmlentities 正确吗? 这就是我所需要的吗? 它如何防止来自here 列出的攻击发送的十六进制、十进制和 base64 编码值?

现在我看到 htmlentities 方法的第三个参数是要在转换中使用的字符集。现在我的站点/数据库是 UTF-8,但提交的表单数据可能不是 UTF-8 编码的,也许他们提交了 ASCII 或 HEX,所以也许我需要先将其转换为 UTF-8?这意味着一些代码,例如:

$encoding = mb_detect_encoding($input);
$input = mb_convert_encoding($input, 'UTF-8', $encoding);
$input = htmlentities($input, ENT_QUOTES, 'UTF-8');

是还是不是?然后我仍然不确定如何防止十六进制、十进制和 base64 可能的 XSS 输入...

如果有一些库或开源 php 框架可以正确地进行 XSS 保护,我很想看看他们是如何在代码中做到这一点的。

非常感谢任何帮助,对于长篇文章感到抱歉!

【问题讨论】:

我真的认为你想多了,正如 Phihag 含蓄地指出的那样,你不应该或不需要两次清理输入。您要么在数据库中清理它(不明智,因为您不会注意到正在发生攻击),要么在将其输出给用户之前对其进行清理。 @gnur 好吧,我正在清理一些字段,然后再进入数据库以保持一些数据完整性。我想如果检测到脚本/HTML 代码,我可以将它们重定向回页面,并让它们在重新提交之前清理输入。我对某些字段(例如用户名)执行此操作,因为用户名只能是字母数字。是否值得尝试检测 XSS 尝试提交并将它们记录在我的应用程序中?我打算在上线之前将 Mod Security 放在网络服务器的前面。我想这会受到一些攻击。 【参考方案1】:

回答这个大胆的问题:是的,有。它叫做htmlspecialchars

需要定期更新 应对新的攻击。

防止 XSS 攻击的正确方法不是对抗特定攻击、过滤/清理数据,而是适当的编码,无处不在。

htmlspecialchars(或htmlentities)结合合理的字符编码决定(即UTF-8)和明确的字符编码规范足以防止所有XSS攻击。幸运的是,在没有显式编码的情况下调用htmlspecialchars(然后假定 ISO-8859-1)恰好也适用于 UTF-8。如果您想明确说明,请创建一个辅助函数:

// Don't forget to specify UTF-8 as the document's encoding
function htmlEncode($s) 
    return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');

哦,为了解决表单问题:不要尝试检测编码,它一定会失败。相反,请以 UTF-8 格式提供表格。然后每个浏览器都会以 UTF-8 格式发送用户输入。

解决具体问题:

(...) 你应该使用 htmlentities 因为 htmlspecialchars 易受 UTF-7 XSS 攻击。

只有在浏览器认为文档以 UTF-7 编码时,才能应用 UTF-7 XSS 漏洞利用。将文档编码指定为 UTF-8(在 HTTP 标头/<head> 之后的元标记中)可以防止这种情况发生。

另外,如果我没有检测到编码, 什么可以阻止攻击者下载 html文件,然后将其更改为 UTF-7 或其他编码,然后 将 POST 请求提交回我的 来自更改后的 html 页面的服务器?

这种攻击场景过于复杂。攻击者只需制作一个 UTF-7 字符串,无需下载任何内容。

如果您接受攻击者的 POST(即您接受匿名的公共用户输入),您的服务器只会将 UTF-7 字符串解释为奇怪的 UTF-8 字符串。这不是问题,攻击者的帖子只会显示乱码。攻击者可以通过提交“grfnlk”一百次来达到相同的效果(发送奇怪的文本)。

如果我的方法只适用于 UTF-8,那么 XSS 攻击就会通过,不是吗?

不,不会。编码不是魔术。编码只是解释二进制字符串的一种方式。例如,字符串“ö”在 UTF-7 中编码为(十六进制)2B 41 50 59(在 UTF-8 中为 C3 B6)。将2B 41 50 59 解码为 UTF-8 会产生“+APY”——无害的、看似随机的字符。

还有 htmlentities 如何防止 HEX 或其他 XSS 攻击?

十六进制数据将照此输出。发送“3C”的攻击者将发布消息“3C”。 “3C”可以变成<,如果您主动尝试解释十六进制输入,例如主动将它们映射到 unicode 代码点然后输出它们。这只是意味着,如果您接受的数据不是纯 UTF-8(例如 base32 编码的 UTF-8),您首先必须解压缩编码,然后然后使用htmlspecialchars在将其包含在 HTML 代码之间之前。

【讨论】:

感谢您的回复。正如我在帖子中提到的,您应该使用 htmlentities,因为 htmlspecialchars 容易受到 UTF-7 XSS 攻击。此外,如果我没有检测到编码,如何阻止攻击者下载 html 文件,然后将其更改为 UTF-7 或其他编码,然后将 POST 请求从更改后的 html 页面提交回我的服务器?如果我的方法只适用于 UTF-8,那么 XSS 攻击就会通过,不是吗?此外,htmlentities 如何防止 HEX 或其他 XSS 攻击? @zoszsoz 更新了答案,驳斥了您评论中的每一点。不要假设魔术;) 好的,非常感谢。对带有 FILTER_FLAG_ENCODE_HIGH 标志的新 PHP 过滤器 FILTER_SANITIZE_SPECIAL_CHARS 有何评论?假设它 HTML 转义 '"<>& 和 ASCII 值小于 32 的字符并编码其他特殊字符。 @zoszsoz 这些过滤器仅在您与无法正确处理“奇怪”字符的同伴交互时才需要。如果您只与网络浏览器通信,则不需要它们。【参考方案2】:

很多安全工程师都建议使用这个库来解决这个特定问题:

https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API

【讨论】:

以上是关于安全的 XSS 清理功能(定期更新)的主要内容,如果未能解决你的问题,请参考以下文章

慎用“360安全卫士”一键清理功能

清理Angular2中的输入[重复]

通过装饰来清理属性以避免 XSS 攻击

在 Rails 中清理输入 XSS 和 HTML 输入

我应该清理降价吗?

清理(MySQL和XSS)