跳到主要内容
版本:7.0.2

ServerWebExchangeFirewall

DeepSeek V3 中英对照 ServerWebExchangeFirewall

恶意用户可以通过多种方式创建请求来利用应用程序。Spring Security 提供了 ServerWebExchangeFirewall 来允许拒绝看起来恶意的请求。默认实现是 StrictServerWebExchangeFirewall,它会拒绝恶意请求。

例如,一个请求可能包含路径遍历序列(如/../)或多个正斜杠(//),这些也可能导致模式匹配失败。某些容器在执行servlet映射之前会将这些序列规范化,但其他容器则不会。为防止此类问题,WebFilterChainProxy使用ServerWebExchangeFirewall策略来检查和包装请求。默认情况下,未规范化的请求会被自动拒绝,并且路径参数会被移除以进行匹配。(因此,例如,原始请求路径/secure;hack=1/somefile.html;hack=2会被返回为/secure/somefile.html。)因此,使用WebFilterChainProxy至关重要。

在实践中,我们建议您在服务层使用方法级安全来控制应用程序的访问权限,而不是完全依赖于在Web应用程序级别定义的安全约束。URL会发生变化,并且很难考虑到应用程序可能支持的所有可能的URL以及请求可能被操纵的方式。您应该限制自己使用一些易于理解的简单模式。始终尝试采用“默认拒绝”的方法,即最后定义一个通配符(/ /**)来拒绝所有访问。

在服务层定义的安全性更为健壮且难以绕过,因此你应该始终充分利用Spring Security的方法安全选项。

你可以通过将 ServerWebExchangeFirewall 暴露为 Bean 来自定义它。

@Bean
public StrictServerWebExchangeFirewall httpFirewall() {
StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
firewall.setAllowSemicolon(true);
return firewall;
}

为防范跨站追踪(XST)HTTP 方法篡改StrictServerWebExchangeFirewall 提供了允许的有效 HTTP 方法列表。默认的有效方法包括 DELETEGETHEADOPTIONSPATCHPOST 以及 PUT。若您的应用需要调整有效方法,可配置自定义的 StrictServerWebExchangeFirewall Bean。以下示例仅允许 HTTP GETPOST 方法:

@Bean
public StrictServerWebExchangeFirewall httpFirewall() {
StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST"));
return firewall;
}

如果你必须允许任何 HTTP 方法(不推荐),可以使用 StrictServerWebExchangeFirewall.setUnsafeAllowAnyHttpMethod(true)。这样做将完全禁用对 HTTP 方法的验证。

StrictServerWebExchangeFirewall 还会检查标头名称与值以及参数名称。它要求每个字符都具有已定义的代码点,且不能是控制字符。

可以通过以下方法根据需要放宽或调整此要求:

  • StrictServerWebExchangeFirewall#setAllowedHeaderNames(Predicate)

  • StrictServerWebExchangeFirewall#setAllowedHeaderValues(Predicate)

  • StrictServerWebExchangeFirewall#setAllowedParameterNames(Predicate)

备注

参数值也可以通过 setAllowedParameterValues(Predicate) 进行控制。

例如,要关闭此检查,您可以使用始终返回 truePredicate 实例来配置您的 StrictServerWebExchangeFirewall

@Bean
public StrictServerWebExchangeFirewall httpFirewall() {
StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
firewall.setAllowedHeaderNames((header) -> true);
firewall.setAllowedHeaderValues((header) -> true);
firewall.setAllowedParameterNames((parameter) -> true);
return firewall;
}

或者,可能存在一个您需要允许的特定值。

例如,iPhone Xʀ 使用的 User-Agent 包含一个不属于 ISO-8859-1 字符集的字符。因此,某些应用服务器会将该值解析为两个独立的字符,其中后一个字符是未定义的字符。

您可以通过 setAllowedHeaderValues 方法来解决这个问题:

@Bean
public StrictServerWebExchangeFirewall httpFirewall() {
StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
Pattern allowed = Pattern.compile("[\\p{IsAssigned}&&[^\\p{IsControl}]]*");
Pattern userAgent = ...;
firewall.setAllowedHeaderValues((header) -> allowed.matcher(header).matches() || userAgent.matcher(header).matches());
return firewall;
}

对于头部值,您也可以考虑在验证时将其解析为 UTF-8:

firewall.setAllowedHeaderValues((header) -> {
String parsed = new String(header.getBytes(ISO_8859_1), UTF_8);
return allowed.matcher(parsed).matches();
});

ServerExchangeRejectedHandler 接口用于处理 Spring Security 的 ServerWebExchangeFirewall 抛出的 ServerExchangeRejectedException。默认情况下,当请求被拒绝时,会使用 HttpStatusExchangeRejectedHandler 向客户端发送 HTTP 400 响应。要自定义此行为,用户可以暴露一个 ServerExchangeRejectedHandler Bean。例如,以下配置将在请求被拒绝时发送 HTTP 404 响应:

@Bean
ServerExchangeRejectedHandler rejectedHandler() {
return new HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND);
}

通过创建自定义的 ServerExchangeRejectedHandler 实现,可以完全自定义处理方式。