如何在 Java 中清理 HTML

每当我们的 Web 应用程序接收到任何将呈现为 HTML 的文本时,我们都必须清理这些文本以避免潜在的XSS 攻击

OWASP提供了一个很好的工具来帮助我们清理 HTML。

概括

  1. 设置项目
  2. 定义清理策略
  3. 编写测试
  4. 结论

设置项目

我们将使用:

这是我们的 Mavenpom.xml文件:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.blebail.blog.sample</groupId>
    <artifactId>java-sanitize-html</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>java-sanitize-html</name>
    <url>https://github.com/baptistelebail/samples/tree/master/java-sanitize-html</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- HTML Sanitizer -->
        <dependency>
            <groupId>com.googlecode.owasp-java-html-sanitizer</groupId>
            <artifactId>owasp-java-html-sanitizer</artifactId>
            <version>20191001.1</version>
        </dependency>

        <!-- JUnit -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.6.0</version>
            <scope>test</scope>
        </dependency>

        <!-- AssertJ -->
        <dependency>
            <groupId>org.assertj</groupId>
            <artifactId>assertj-core</artifactId>
            <version>3.15.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
        </plugins>
    </build>
</project>

定义清理策略

假设我们的 Web 应用程序是一个社区网站,其中包含文章和评论。

我们的应用程序将在三个用例中处理和呈现 HTML:

  1. 用户提交评论
  2. 一位出版商提交了一篇文章
  3. 用户更新其个人资料描述(我们为此提供<my-element>用户可以使用的组件)

我们将分别需要 3 项清理策略:

  1. 从评论中删除任何 HTML 的严格政策
  2. 允许在文章中找到常见 HTML 元素的策略(标题、段落、图像、链接……)
  3. 只允许我们的元素的自定义策略<my-element>

我们创建SanitizationPolicy接口:

public interface SanitizationPolicy {

    /**
     * Sanitizes the string according to the policy
     * @param input the input string to be sanitized
     * @return the sanitized string
     */
    String sanitize(String input);
}

这将简单地清理输入字符串。

OWASP HTML Sanitizer提供了几种创建清理策略的方法(OWASP 命名为PolicyFactory):我们可以通过HtmlPolicyBuilder手动创建的策略,或者通过Sanitizers.*的预制策略,可以与and() 方法结合使用。

我们将使用EnumSanitizationPolicy实现接口,该接口将具有三个值:

  1. STRICT
  2. ARTICLE
  3. CUSTOM

每一个都与特定的PolicyFactory相关联。

我们创建HtmlSanitizationPolicy枚举:

import org.owasp.html.HtmlPolicyBuilder;
import org.owasp.html.PolicyFactory;
import org.owasp.html.Sanitizers;

public enum HtmlSanitizationPolicy implements SanitizationPolicy {

    STRICT(new HtmlPolicyBuilder()
            .toFactory()),

    ARTICLE(Sanitizers.BLOCKS
            .and(Sanitizers.FORMATTING)
            .and(Sanitizers.STYLES)
            .and(Sanitizers.IMAGES)
            .and(Sanitizers.LINKS)),

    CUSTOM(new HtmlPolicyBuilder()
            .allowElements("my-element")
            .toFactory());

    private final PolicyFactory policyFactory;

    HtmlSanitizationPolicy(PolicyFactory policyFactory) {
        this.policyFactory = policyFactory;
    }

    @Override
    public String sanitize(String input) {
        return policyFactory.sanitize(input);
    }
}

根据我们的政策,我们现在可以使用HtmlSanitizationPolicy.<POLICY>.sanitize(...).

编写测试

我们将通过一些测试来验证我们的政策:

  1. 我们的政策不允许使用链接 ( <a>) 和 JavaScript ( )<script>STRICT
  2. <script>我们的政策不允许使用JavaScript ( ),ARTICLE但常见的文章元素 ( <p>, <strong>, style="...", <img>, <a>, <h1>, <h2>, ...) 是允许的。
    我们将使用 JUnit 5 ParameterizedTest并使用@ValueSource 测试一些示例
  3. 我们的政策不允许使用链接 ( <a>) 和 JavaScript ( ),但<script>CUSTOM<my-element>

我们创建HtmlSanitizationPolicyTestsrc/test/java

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import static org.assertj.core.api.Assertions.assertThat;

public final class HtmlSanitizationPolicyTest {

    @Test
    public void shouldNotAllowLinksOrJavaScriptOnStrictPolicy() {
        String text = "Text with <a href=\"https://example.com\">a link</a> " +
                "and<script>alert('javascript');</script>";

        String sanitized = HtmlSanitizationPolicy.STRICT.sanitize(text);

        assertThat(sanitized).isEqualTo("Text with a link and");
    }

    @Test
    public void shouldNotAllowJavaScriptOnArticlePolicy() {
        String text = "Text with <a href=\"https://example.com\" rel=\"nofollow\">a link</a> " +
                "and<script>alert('javascript');</script>";

        String sanitized = HtmlSanitizationPolicy.ARTICLE.sanitize(text);

        assertThat(sanitized).isEqualTo("Text with <a href=\"https://example.com\" rel=\"nofollow\">a link</a> and");
    }

    @ParameterizedTest
    @ValueSource(strings = {
            "A <h1>Title</h1> and a <p>paragraph</p>",
            "<strong>Strong</strong> and <em>emphasized</em>",
            "Code with <span style=\"color:red\">style</span>",
            "An <img src=\"https://example.com/img.jpg\" width=\"200\" />",
            "A <a href=\"https://example.com\" rel=\"nofollow\">link</a>"
    })
    public void shouldAllowCommonArticleElementsOnArticlePolicy(String text) {
        String sanitized = HtmlSanitizationPolicy.ARTICLE.sanitize(text);

        assertThat(sanitized).isEqualTo(text);
    }

    @Test
    public void shouldNotAllowLinksOrJavaScriptOnCustomPolicy() {
        String text = "Text with <a href=\"https://example.com\" rel=\"nofollow\">a link</a> " +
                "and<script>alert('javascript');</script> and <my-element>Mine</my-element>";

        String sanitized = HtmlSanitizationPolicy.CUSTOM.sanitize(text);

        assertThat(sanitized).isEqualTo("Text with a link and and <my-element>Mine</my-element>");
    }

    @Test
    public void shouldAllowMyElementOnCustomPolicy() {
        String text = "Text with <my-element>Mine</my-element>";

        String sanitized = HtmlSanitizationPolicy.CUSTOM.sanitize(text);

        assertThat(sanitized).isEqualTo(text);
    }
}

概括

OWASP HTML Sanitizer允许我们非常快速地创建 HTML 清理策略,覆盖最常见的需求,如果我们想要自定义策略以允许任何元素上的任何属性,它还提供了一个远远超出的 API。

(整个项目的源代码都可以在这里找到

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值