杰瑞科技汇

Java的XSSProtect如何防护XSS攻击?

XSS 保护是 Web 开发中至关重要的安全环节,在 Java 生态中,有多种方式可以有效地防御 XSS 攻击,从框架内置的功能到手动编码,再到专门的库。

Java的XSSProtect如何防护XSS攻击?-图1
(图片来源网络,侵删)

核心思想:XSS 攻击与防御原理

XSS 攻击:攻击者将恶意脚本(通常是 JavaScript)注入到网页中,当其他用户访问该网页时,浏览器会执行这些恶意脚本,从而可能导致会话劫持、数据窃取、钓鱼网站等严重后果。

防御核心:对用户输入的所有数据进行转义,确保浏览器将其视为纯文本内容,而不是可执行的 HTML/JavaScript 代码。

  • 用户输入 <script>alert('XSS')</script>
  • 经过转义后,变成 &lt;script&gt;alert(&#x27;XSS&#x27;)&lt;/script&gt;
  • 浏览器渲染时,会显示 <script>alert('XSS')</script> 这段文本,而不会执行其中的脚本。

Java 中的 XSS 保护方案

下面我们按照从简单到复杂的顺序,介绍几种主流的 Java XSS 保护方案。

使用框架内置的 XSS 保护功能(推荐)

现代 Web 框架已经内置了强大的 XSS 保护机制,这是最简单、最可靠的方法。

Java的XSSProtect如何防护XSS攻击?-图2
(图片来源网络,侵删)

Spring Boot

Spring Boot 3.x 及更高版本,基于 Jakarta EE 10,内置了 Jakarta EE 的 XSS 过滤器

如何启用:

application.propertiesapplication.yml 中配置:

# application.properties
# 启用 Jakarta EE 的 XSS 过滤器
spring.web.xss.enabled=true

这个过滤器默认会对所有请求参数、请求头和 Cookie 进行基本的 XSS 清理,它会尝试移除或转义已知的 XSS 攻击载荷。

Java的XSSProtect如何防护XSS攻击?-图3
(图片来源网络,侵删)

更强大的方案:OWASP Java HTML Sanitizer

Spring Boot 默认的过滤器虽然方便,但功能相对基础,对于安全性要求极高的场景,推荐使用 OWASP Java HTML Sanitizer

如何集成:

  1. 添加依赖:

    <dependency>
        <groupId>org.owasp.html</groupId>
        <artifactId>html-sanitizer</artifactId>
        <version>20250608.1</version> <!-- 使用最新版本 -->
    </dependency>
  2. 创建一个工具类:

    import org.owasp.html.PolicyFactory;
    import org.owasp.html.Sanitizers;
    public class HtmlSanitizerUtil {
        // 定义一个策略:允许 a 标签,并允许其 href 属性;允许 img 标签,并允许其 src 属性;允许 p 和 br 标签。
        private static final PolicyFactory policy = Sanitizers.FORMATTING
                .and(Sanitizers.LINKS)
                .and(Sanitizers.IMAGES)
                .and(Sanitizers.BLOCKS);
        /**
         * 清理 HTML 字符串,只允许预定义的安全标签和属性。
         * @param html 原始 HTML 字符串
         * @return 清理后的安全 HTML 字符串
         */
        public static String sanitize(String html) {
            if (html == null) {
                return null;
            }
            return policy.sanitize(html);
        }
    }
  3. 在 Controller 中使用:

    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    @RestController
    public class CommentController {
        @PostMapping("/comment")
        public String postComment(@RequestParam("content") String content) {
            // 对用户提交的富文本内容进行清理
            String safeContent = HtmlSanitizerUtil.sanitize(content);
            // 将 safeContent 存入数据库或进行其他处理
            // ...
            return "Comment received and sanitized: " + safeContent;
        }
    }

优点

  • 高度可定制:你可以精确控制允许哪些 HTML 标签和属性。
  • 功能强大:专门为清理 HTML 而设计,比简单的转义更智能。
  • 社区标准:由 OWASP(开放式 Web 应用程序安全项目)维护,是业界公认的最佳实践之一。

Jakarta EE (原 Java EE)

如果你使用的是 Jakarta EE (如 Tomcat 10+, WildFly),可以直接使用 jakarta.servlet.http.HttpServletRequestWrapperjakarta.servlet.Filter 来实现 XSS 过滤。

这是一个通用的 Servlet 过滤器实现,不仅适用于 Spring。

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
public class XssFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        chain.doFilter(new XssRequestWrapper((HttpServletRequest) request), response);
    }
    // ... init 和 destroy 方法 ...
}
// 包装器,用于转义请求参数
class XssRequestWrapper extends HttpServletRequestWrapper {
    public XssRequestWrapper(HttpServletRequest request) {
        super(request);
    }
    @Override
    public String getParameter(String name) {
        String value = super.getParameter(name);
        return xssEncode(value);
    }
    @Override
    public String[] getParameterValues(String name) {
        String[] values = super.getParameterValues(name);
        if (values == null) {
            return null;
        }
        String[] encodedValues = new String[values.length];
        for (int i = 0; i < values.length; i++) {
            encodedValues[i] = xssEncode(values[i]);
        }
        return encodedValues;
    }
    // 简单的转义方法
    private String xssEncode(String value) {
        if (value == null) {
            return null;
        }
        // 这里使用更全面的转义逻辑,例如使用 Apache Commons Lang 的 StringEscapeUtils
        // 或者 OWASP ESAPI (已不推荐) 或自定义转义
        return value.replace("<", "&lt;")
                     .replace(">", "&gt;")
                     .replace("\"", "&quot;")
                     .replace("'", "&#x27;")
                     .replace("/", "&#x2F;");
    }
}

然后在 web.xml 中注册这个过滤器。


输出时转义(Contextual Output Encoding)

这是防御 XSS 的黄金法则,无论你如何对输入进行验证和清理,最终在将数据输出到 HTML、JavaScript、CSS 或 URL 上下文时,都必须进行正确的上下文转义。

JSP/Thymeleaf 模板引擎中的转义

  • JSP: JSP 默认会对 EL 表达式 的输出进行 HTML 转义,如果你需要关闭或进行其他转义,可以使用 JSTL 的 <c:out>

      <%-- 默认情况,EL 会自动转义 --%>
      <p>${userComment}</p>
      <%-- 显式地进行 HTML 转义 --%>
      <p><c:out value="${userComment}" /></p>
      <%-- 如果想在 JavaScript 上下文中使用,需要手动进行 JS 转义 --%>
      <script>
          var comment = "${jsEscapeUtils.escapeJavaScript(userComment)}";
      </script>
  • Thymeleaf: Thymeleaf 默认会进行 HTML 转义,它的语法非常清晰。

      <!-- 默认 HTML 转义 -->
      <p th:text="${userComment}">Default Comment</p>
      <!-- 不进行转义 (危险!仅当 100% 确保内容是可信的时使用) -->
      <p th:utext="${trustedHtmlContent}">Trusted Content</p>
      <!-- 在 JavaScript 上下文中转义 -->
      <script th:inline="javascript">
          var comment = /*[[${userComment}]]*/ 'Default comment';
          // Thymeleaf 会自动将 [[...]] 中的内容进行 JavaScript 转义
      </script>

使用 Apache Commons Lang 或其他工具库进行手动转义

如果你在代码中(构建 JSON 响应时)需要手动转义,可以使用工具库。

添加依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.10.0</version> <!-- 使用最新版本 -->
</dependency>

使用示例:

import org.apache.commons.text.StringEscapeUtils;
public class XssProtectionExample {
    public String buildJsonResponse(String userInput) {
        // 1. 对输入进行清理或转义
        String safeInput = StringEscapeUtils.escapeHtml4(userInput);
        // 2. 在构建 JSON 时,确保字符串值被正确转义
        // 大多数 JSON 库(如 Jackson, Gson)会自动处理这个转义过程
        String json = String.format("{\"message\": \"%s\"}", safeInput);
        return json;
    }
}

注意: 像 Jackson 或 Gson 这样的 JSON 库在将 Java 对象序列化为 JSON 字符串时,会自动对字符串中的特殊字符进行转义,以生成合法的 JSON,你只需要确保传递给这些库的 Java 对象属性是“干净”的即可。


总结与最佳实践

方案 优点 缺点 适用场景
框架内置 XSS 过滤器 开箱即用,简单方便 功能可能不够强大和灵活 快速开发,对安全性要求不是极端高的项目
OWASP Java HTML Sanitizer 功能强大,高度可定制,社区标准 需要额外引入依赖,学习一点配置 强烈推荐,所有需要处理富文本内容的场景。
输出时转义 防御的最后一道防线,非常可靠 需要在模板中或代码中手动处理,容易遗漏 必须使用,所有将数据输出到页面的地方,是防御 XSS 的核心。
输入验证/清理 主动防御,减少“脏数据”进入系统 不能完全依赖,因为总有绕过的可能 在数据进入系统(如存入数据库)前进行,作为防御的一部分。

终极最佳实践组合拳:

  1. 输入端(防御纵深):

    • 对所有用户输入进行严格的白名单验证(只允许字母、数字、特定符号)。
    • 对于富文本输入,使用 OWASP Java HTML Sanitizer 进行清理,只保留你允许的 HTML 标签和属性。
  2. 输出端(黄金法则):

    • 永远不要相信任何数据,无论数据经过了多少层处理,在将其输出到浏览器时,都必须根据其上下文(HTML, JavaScript, CSS, URL)进行正确的转义
    • 在 JSP/Thymeleaf 等模板引擎中,优先使用它们提供的自动转义功能(如 th:text)。
    • 如果手动构建响应(如 JSON),确保你的库(如 Jackson)能正确处理转义,或者手动调用 StringEscapeUtils
  3. 安全头:

    • 在响应头中设置 Content-Security-Policy (CSP),这是一种强大的 XSS 缓解技术,可以限制浏览器只加载来自可信来源的资源,是防御 XSS 的最后一道防线。
    • 设置 X-XSS-Protection: 1; mode=block (现代浏览器已弃用,但无害) 和 X-Content-Type-Options: nosniff

没有单一的银弹,最安全的系统是采用了多层防御策略的系统,输入清理 + 输出转义 + 安全头 = 强大的 XSS 防御体系。

分享:
扫描分享到社交APP
上一篇
下一篇