返回更新列表
新发布Sep 22, 2026

java-html-sanitizer v20260921.1

获取第三方 HTML 并生成可安全嵌入到 Web 应用中的 HTML。快速且易于配置。

分享

OWASP Java HTML Sanitizer

Build OpenSSF Best Practices Maven Central

一个用 Java 编写、快速且易于配置的 HTML Sanitizer,让你可以在 Web 应用中包含第三方编写的 HTML,同时防御 XSS。

该 sanitizer JAR 没有运行时依赖。它唯一的编译时依赖是 spotbugs-annotations(provided 作用域,仅注解);其他 jar 仅测试套件需要。

此代码在编写时遵循安全最佳实践,拥有广泛的测试套件,并经过了对抗性安全审查。

目录

快速开始

快速开始 包含有关如何在使用或不使用 Maven 的情况下入门的说明。

仓库布局

构建需要 JDK 11 或更高版本。从仓库根目录,构建所有 Maven 模块并运行其测试,使用:

./mvnw clean verify

Maven wrapper 固定了所需的 Maven 版本。如果你使用 -pl 选择模块,请同时使用 -am,以便 Maven 构建它所依赖的模块。

  • owasp-java-html-sanitizer/ 包含已发布的库、其测试以及其 Java 模块描述符。
  • java8-shim/ 和 java10-shim/ 实现了 打包进库 JAR 的兼容性代码。
  • examples/ 包含可供阅读和复制的示例策略; 它会被测试,但不会被发布。
  • empiricism/ 包含用于 生成 sanitizer 的 HTML 元素表的浏览器实验。
  • docs/ 包含用户、安全和历史文档。

预打包策略

你可以使用 预打包策略:

PolicyFactory policy = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
String safeHTML = policy.sanitize(untrustedHTML);

制定策略

这些 测试 展示了如何配置你自己的 策略:

PolicyFactory policy = new HtmlPolicyBuilder()
    .allowElements("a")
    .allowUrlProtocols("https")
    .allowAttributes("href").onElements("a")
    .requireRelNofollowOnLinks()
    .toFactory();
String safeHTML = policy.sanitize(untrustedHTML);

自定义策略

你可以编写 自定义策略 来实现诸如将 h1 改为带有特定 class 的 div 之类的操作:

PolicyFactory policy = new HtmlPolicyBuilder()
    .allowElements("p")
    .allowElements(
        (String elementName, List<String> attrs) -> {
          // Add a class attribute.
          attrs.add("class");
          attrs.add("header-" + elementName);
          // Return elementName to include, null to drop.
          return "div";
        }, "h1", "h2", "h3", "h4", "h5", "h6")
    .toFactory();
String safeHTML = policy.sanitize(untrustedHTML);

请注意,如果你希望这些元素在不包含任何属性的情况下也能通过过滤器,就需要使用 allowWithoutAttributes() 方法将 "a"、"font"、"img"、"input" 和 "span" 这些元素显式加入白名单。

属性策略 也允许运行自定义代码。添加属性策略不会削弱任何默认策略,例如 style 或 URL 属性检查。

PolicyFactory myPolicy = new HtmlPolicyBuilder()
    .allowElements("div", "span")
    .allowAttributes("data-foo")
        .matching(
            (String elementName, String attributeName, String value) -> {
              // Return value for the attribute or null to drop.
              return value;
            })
        .onElements("div", "span")
    .toFactory();

预处理器

预处理器允许插入文本以及进行大规模的结构性更改。

PolicyFactory myPolicy = new HtmlPolicyBuilder()
    .withPreprocessor(
        (HtmlStreamEventReceiver r) -> {
          // Provide user with info about links before they click.
          // Before:                       <a href="https://example.com/...">
          // After:  (https://example.com) <a href="https://example.com/...">
          return new HtmlStreamEventReceiverWrapper(r) {
            @Override public void openTag(String elementName, List<String> attrs) {
              if ("a".equals(elementName)) {
                for (int i = 0, n = attrs.size(); i < n; i += 2) {
                  if ("href".equals(attrs.get(i))) {
                    String url = attrs.get(i + 1);
                    String origin;
                    try {
                      URI uri = new URI(url);
                      String scheme = uri.getScheme();
                      String authority = uri.getRawAuthority();
                      if (scheme == null && authority == null) {
                        origin = null;
                      } else {
                        origin = (scheme != null ? scheme + ":" : "")
                               + (authority != null ? "//" + authority : "");
                      }
                    } catch (URISyntaxException ex) {
                      origin = "about:invalid";
                    }
                    if (origin != null) {
                      text(" (" + origin + ") ");
                    }
                  }
                }
              }
              super.openTag(elementName, attrs);
            }
          };
        })
     .allowElements("a")
     .allowAttributes("href").onElements("a")
     .allowStandardUrlProtocols()
    ...
    .toFactory();

预处理在应用策略之前进行,因此不会影响输出的安全性。

遥测

当策略拒绝某个元素或属性时,它会通知 HtmlChangeListener。

你可以利用这一点来跟踪策略违规趋势,并发现何时有人试图突破你的安全防护。

PolicyFactory myPolicyFactory = ...;
// If you need to associate reports with some context, you can do so.
MyContextClass myContext = ...;

String sanitizedHtml = myPolicyFactory.sanitize(
    unsanitizedHtml,
    new HtmlChangeListener<MyContextClass>() {
      @Override
      public void discardedTag(MyContextClass context, String elementName) {
        // ...
      }
      @Override
      public void discardedAttributes(
          MyContextClass context, String elementName, String... attributeNames) {
        // ...
      }
    },
    myContext);

discardedAttributes 也会对因元素被丢弃(因其已无剩余属性)而被拒绝的属性触发,例如一个链接的唯一 href 被拒绝的情况。有两个默认方法为覆盖它们的监听器提供更多细节:discardedAttribute 接收每个被拒绝属性的值,discardedText 接收策略从保留的字面内容元素中移除的类标签内容,以及渲染器无法从其中输出的内容。

注意:如果字符串经过净化后没有任何变更通知,并不意味着输入字符串一定可以安全使用。只能使用净化器的输出。

净化器确保输出属于常用 HTML 解析器对其含义能达成一致的 HTML 子集,但没有通知并不意味着输入属于这样的子集,只意味着它不包含被移除的结构性内容。

关于此主题的更多内容,请参阅 "Why sanitize when you can validate"。

有问题?

如果您希望报告漏洞,请参阅 安全策略 和 攻击审查基本规则。

订阅 邮件列表 或关注本仓库的 发布 和 安全公告 以获取已知 漏洞 和重要更新的通知。

贡献

分类