一个用于对来自不可信来源的 HTML 执行快速、可配置清洗的库。支持 Java 7 及以上版本。
换一种说法:它是一个 API,可帮助您确保客户端在其个人资料、评论等中提供的 HTML 中不包含恶意代码,这些内容会持久化存储在服务器上。在 Web 应用中,“恶意代码”通常指“JavaScript”。大多数情况下,级联样式表(CSS)只有在调用 JavaScript 时才会被视为恶意。然而,在很多情况下,“正常”的 HTML 和 CSS 也可能被以恶意方式使用。
首先,从 Maven 添加依赖:
<dependency>
<groupId>org.owasp.antisamy</groupId>
<artifactId>antisamy</artifactId>
<version>LATEST_VERSION</version>
</dependency>
您网站对 AntiSamy 的使用场景很可能至少与预定义策略文件之一大致相似。每个策略文件都代表一种允许用户提供 HTML(以及可能的 CSS)格式信息的“典型”场景。让我们来看看不同的策略文件:
Slashdot 是一个科技新闻网站,允许用户以非常有限的 HTML 标记匿名回复新闻帖子。Slashdot 不仅是酷站之一,也是遭受过许多不同成功攻击的网站之一。Slashdot 的规则相当严格:用户只能提交以下 HTML 标签,且不能使用 CSS:<b>、<u>、<i>、<a>、<blockquote>。
因此,我们构建了一个允许提供类似功能的策略文件。所有直接作用于字体、颜色或强调的文本格式标签都被允许。
eBay 是我所知最受欢迎的在线拍卖网站。它是一个公共网站,因此任何人都可以发布包含丰富 HTML 内容的列表。鉴于 eBay 作为攻击目标的吸引力,它曾遭受过几次复杂的 XSS 攻击也就不足为奇了。与 Slashdot 相比,列表允许包含更丰富的内容——因此其攻击面要大得多。
MySpace 在这个项目诞生时是最受欢迎的社交网站。用户被允许提交几乎所有他们想要的 HTML 和 CSS——只要不包含 JavaScript。MySpace 当时使用单词黑名单来验证用户的 HTML,这就是为什么他们遭受了臭名昭著的 Samy 蠕虫攻击。Samy 蠕虫利用分段攻击(fragmentation attacks)结合一个本应被列入黑名单的单词(eval)——正是这个项目灵感的来源。
我不知道这个策略文件有什么实际使用场景。如果您想允许每一个有效的 HTML 和 CSS 元素(但不允许 JavaScript 或明显的 CSS 相关网络钓鱼攻击),可以使用这个策略文件。连 MySpace 都没有这么疯狂。不过,它确实是一个很好的参考,因为它包含每个元素的基础规则,因此在定制其他策略文件时可以将其用作知识库。
在改进 AntiSamy 策略文件的 XML 架构定义(XSD)时,我们注意到 AntiSamy 实际上并未强制执行 XSD。因此,从 AntiSamy 1.6.0 开始,我们更改了默认行为以强制执行架构,如果 AntiSamy 策略无效则不再继续。但是……
我们认识到,如果开发者的 AntiSamy 策略不合规,他们可能无法立即修复,但仍希望升级 AntiSamy 以获取安全改进、功能增强和错误修复。因此,我们提供了两种(临时!)禁用架构验证的方法:
将 Java 系统属性 owasp.validator.validateschema 设置为 false。可以在命令行(例如,-Dowasp.validator.validateschema=false)或通过 Java 系统属性文件完成。两者都不需要更改代码。
修改使用 AntiSamy 的代码,在加载 AntiSamy 策略之前调用:Policy.setSchemaValidation(false)。这是一个静态调用,因此一旦禁用,所有新的 Policy 实例都将被禁用。
为了鼓励 AntiSamy 用户只使用符合 XSD 的策略,AntiSamy 在禁用架构验证时始终会记录某种类型的警告。它要么 WARN 策略不合规以便修复,要么 WARN 策略合规但架构验证已关闭,因此应重新开启验证(即停止禁用)。我们还添加了在加载和验证 AntiSamy 架构时的 INFO 级别日志记录。
禁用新架构验证功能的能力是临时性的,旨在平滑过渡到完全有效的 AntiSamy 策略文件。我们计划在下一个主版本中移除该功能。我们估计这将在 2022 年中后期,所以不是很快。这样做的目的是让直接使用 AntiSamy 或通过 ESAPI 等其他库使用 AntiSamy 的开发团队有充足的时间在架构验证成为必需之前使其策略文件符合架构要求。
这在 1.6.1 中很快得到修复,改为仅使用 slf4j API。AntiSamy 现在包含 slf4j-simple 库用于日志记录,但 AntiSamy 用户如果愿意,也可以导入并使用其他兼容 slf4j 的日志库。如果需要,他们还可以排除 slf4j-simple。
警告:AntiSamy 使用 slf4j-simple 在没有任何配置文件的情况下,会以缓冲方式将日志消息输出到标准输出。因此,如果抛出异常(例如 PolicyException),部分或全部日志消息可能会丢失。这可以通过将 slf4j-simple 配置为记录到标准错误,或使用其他这样做的 slf4j 记录器来解决。
您可能希望以默认配置部署 AntiSamy,但网站同样可能希望对用户允许的内容制定严格的、业务驱动的规则。决定定制的讨论还应考虑攻击面——攻击面与策略文件的规模成相对比例增长。
使用 AntiSamy 很容易。以下是通过策略文件调用 AntiSamy 的示例:
import org.owasp.validator.html.*;
Policy policy = Policy.getInstance(POLICY_FILE_LOCATION);
AntiSamy as = new AntiSamy();
CleanResults cr = as.scan(dirtyInput, policy);
MyUserDAO.storeUserProfile(cr.getCleanHTML()); // some custom function
有几种方法可以创建 Policy 对象。getInstance() 方法可以接受以下任何一种:
String 文件名File 对象InputStreamAntiSamy#scan() 方法传递第二个参数来按文件名引用 Policy 文件:AntiSamy as = new AntiSamy();
CleanResults cr = as.scan(dirtyInput, policyFilePath);
最后,策略文件也可以在第二个参数中直接通过 File 对象引用:
AntiSamy as = new AntiSamy();
CleanResults cr = as.scan(dirtyInput, new File(policyFilePath));
CleanResults 对象提供了很多有用的东西。
getErrorMessages() - String 错误消息列表 -- 如果返回 0,并不意味着没有攻击!getCleanHTML() - 干净、安全的 HTML 输出getCleanXMLDocumentFragment() - 干净、安全的 XMLDocumentFragment,反映在 getCleanHTML() 中getScanTime() - 返回扫描时间(秒)重要说明:关于 getErrorMessages() 方法一直存在很多困惑。getErrorMessages() 方法返回空列表并不表示“这是安全输入?”的肯定回答。您必须始终使用经过净化的输入,并且无法确定传入的输入没有遭受攻击。
对净化器有效性至关重要的序列化和反序列化过程是有意有损的,会通过多种攻击向量过滤掉攻击。不幸的是,这种策略的代价之一是我们事后并不总能知道曾经发生过攻击。因此,getErrorMessages() API 的存在是为了帮助用户理解其善意的输入是否符合系统的要求,而不是帮助开发者检测是否存在攻击。
其他文档可在该 Github 项目的 wiki 页面获取:https://github.com/nahsra/antisamy/wiki 以及 OWASP AntiSamy 项目页面:https://owasp.org/www-project-antisamy/
如果您发现了 Bug,请在 AntiSamy 仓库中创建 Issue:https://github.com/nahsra/antisamy/issues
如果您在 AntiSamy 中发现了漏洞,请先搜索 Issue 列表(见上文),查看是否已被报告。如果尚未报告,请直接联系 Dave Wichers(dave.wichers at owasp.org)。请不要通过 GitHub Issues 报告漏洞,因为我们希望在补丁实施和部署期间保证用户安全。如果您希望因发现漏洞而获得致谢,请遵循此流程。
更多详细信息请参阅文件:SECURITY.md。
您可以很容易地从源代码构建和测试:
$ git clone https://github.com/nahsra/antisamy
$ cd antisamy
$ mvn package
根据 BSD-3-Clause 许可证发布,具体规定见:LICENSE。