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

核心思想:XSS 攻击与防御原理
XSS 攻击:攻击者将恶意脚本(通常是 JavaScript)注入到网页中,当其他用户访问该网页时,浏览器会执行这些恶意脚本,从而可能导致会话劫持、数据窃取、钓鱼网站等严重后果。
防御核心:对用户输入的所有数据进行转义,确保浏览器将其视为纯文本内容,而不是可执行的 HTML/JavaScript 代码。
- 用户输入
<script>alert('XSS')</script> - 经过转义后,变成
<script>alert('XSS')</script> - 浏览器渲染时,会显示
<script>alert('XSS')</script>这段文本,而不会执行其中的脚本。
Java 中的 XSS 保护方案
下面我们按照从简单到复杂的顺序,介绍几种主流的 Java XSS 保护方案。
使用框架内置的 XSS 保护功能(推荐)
现代 Web 框架已经内置了强大的 XSS 保护机制,这是最简单、最可靠的方法。

Spring Boot
Spring Boot 3.x 及更高版本,基于 Jakarta EE 10,内置了 Jakarta EE 的 XSS 过滤器。
如何启用:
在 application.properties 或 application.yml 中配置:
# application.properties # 启用 Jakarta EE 的 XSS 过滤器 spring.web.xss.enabled=true
这个过滤器默认会对所有请求参数、请求头和 Cookie 进行基本的 XSS 清理,它会尝试移除或转义已知的 XSS 攻击载荷。

更强大的方案:OWASP Java HTML Sanitizer
Spring Boot 默认的过滤器虽然方便,但功能相对基础,对于安全性要求极高的场景,推荐使用 OWASP Java HTML Sanitizer。
如何集成:
-
添加依赖:
<dependency> <groupId>org.owasp.html</groupId> <artifactId>html-sanitizer</artifactId> <version>20250608.1</version> <!-- 使用最新版本 --> </dependency> -
创建一个工具类:
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); } } -
在 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.HttpServletRequestWrapper 和 jakarta.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("<", "<")
.replace(">", ">")
.replace("\"", """)
.replace("'", "'")
.replace("/", "/");
}
}
然后在 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 的核心。 |
| 输入验证/清理 | 主动防御,减少“脏数据”进入系统 | 不能完全依赖,因为总有绕过的可能 | 在数据进入系统(如存入数据库)前进行,作为防御的一部分。 |
终极最佳实践组合拳:
-
输入端(防御纵深):
- 对所有用户输入进行严格的白名单验证(只允许字母、数字、特定符号)。
- 对于富文本输入,使用 OWASP Java HTML Sanitizer 进行清理,只保留你允许的 HTML 标签和属性。
-
输出端(黄金法则):
- 永远不要相信任何数据,无论数据经过了多少层处理,在将其输出到浏览器时,都必须根据其上下文(HTML, JavaScript, CSS, URL)进行正确的转义。
- 在 JSP/Thymeleaf 等模板引擎中,优先使用它们提供的自动转义功能(如
th:text)。 - 如果手动构建响应(如 JSON),确保你的库(如 Jackson)能正确处理转义,或者手动调用
StringEscapeUtils。
-
安全头:
- 在响应头中设置
Content-Security-Policy (CSP),这是一种强大的 XSS 缓解技术,可以限制浏览器只加载来自可信来源的资源,是防御 XSS 的最后一道防线。 - 设置
X-XSS-Protection: 1; mode=block(现代浏览器已弃用,但无害) 和X-Content-Type-Options: nosniff。
- 在响应头中设置
没有单一的银弹,最安全的系统是采用了多层防御策略的系统,输入清理 + 输出转义 + 安全头 = 强大的 XSS 防御体系。
