此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.4.3spring-doc.cadn.net.cn

ServerWebExchange防火墙

恶意用户可以通过多种方式创建请求,从而利用应用程序。 Spring Security 提供了ServerWebExchangeFirewall允许拒绝看似恶意的请求。 默认实现为StrictServerWebExchangeFirewall拒绝恶意请求。spring-doc.cadn.net.cn

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

在实践中,我们建议您在服务层使用方法安全性来控制对应用程序的访问,而不是完全依赖于在 Web 应用程序级别定义的安全约束的使用。 URL 会发生变化,因此很难考虑应用程序可能支持的所有可能的 URL 以及可能如何处理请求。 您应该限制自己使用一些易于理解的简单模式。 始终尝试使用“默认拒绝”方法,其中您有一个全能通配符 (/) 最后定义以拒绝访问。spring-doc.cadn.net.cn

在服务层定义的安全性更加健壮且更难绕过,因此您应该始终利用 Spring Security 的方法安全选项。spring-doc.cadn.net.cn

您可以自定义ServerWebExchangeFirewall将其公开为 Bean。spring-doc.cadn.net.cn

允许矩阵变量
@Bean
public StrictServerWebExchangeFirewall httpFirewall() {
    StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
    firewall.setAllowSemicolon(true);
    return firewall;
}
@Bean
fun httpFirewall(): StrictServerWebExchangeFirewall {
    val firewall = StrictServerWebExchangeFirewall()
    firewall.setAllowSemicolon(true)
    return firewall
}

为了防止跨站点跟踪 (XST)HTTP 动词篡改StrictServerWebExchangeFirewall提供允许的有效 HTTP 方法的允许列表。 默认的有效方法是DELETE,GET,HEAD,OPTIONS,PATCH,POSTPUT. 如果您的应用程序需要修改有效的方法,您可以配置自定义的StrictServerWebExchangeFirewall豆。 以下示例仅允许 HTTPGETPOST方法:spring-doc.cadn.net.cn

只允许 GET & POST
@Bean
public StrictServerWebExchangeFirewall httpFirewall() {
    StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
    firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST"));
    return firewall;
}
@Bean
fun httpFirewall(): StrictServerWebExchangeFirewall {
    val firewall = StrictServerWebExchangeFirewall()
    firewall.setAllowedHttpMethods(listOf("GET", "POST"))
    return firewall
}

如果必须允许任何 HTTP 方法(不推荐),则可以使用StrictServerWebExchangeFirewall.setUnsafeAllowAnyHttpMethod(true). 这样做会完全禁用 HTTP 方法的验证。spring-doc.cadn.net.cn

StrictServerWebExchangeFirewall此外,还会检查标头名称和值以及参数名称。 它要求每个字符都有一个定义的码位,而不是控制字符。spring-doc.cadn.net.cn

该要求可根据需要通过以下方法放宽或调整:spring-doc.cadn.net.cn

参数值也可以通过setAllowedParameterValues(Predicate).spring-doc.cadn.net.cn

例如,要关闭此检查,您可以将StrictServerWebExchangeFirewallPredicate始终返回true:spring-doc.cadn.net.cn

允许任何标头名称、标头值和参数名称
@Bean
public StrictServerWebExchangeFirewall httpFirewall() {
    StrictServerWebExchangeFirewall firewall = new StrictServerWebExchangeFirewall();
    firewall.setAllowedHeaderNames((header) -> true);
    firewall.setAllowedHeaderValues((header) -> true);
    firewall.setAllowedParameterNames((parameter) -> true);
    return firewall;
}
@Bean
fun httpFirewall(): StrictServerWebExchangeFirewall {
    val firewall = StrictServerWebExchangeFirewall()
    firewall.setAllowedHeaderNames { true }
    firewall.setAllowedHeaderValues { true }
    firewall.setAllowedParameterNames { true }
    return firewall
}

或者,可能需要允许特定值。spring-doc.cadn.net.cn

例如,iPhone Xʀ 使用User-Agent这包括不在 ISO-8859-1 字符集中的字符。 由于这个事实,一些应用程序服务器将此值解析为两个单独的字符,后者是未定义的字符。spring-doc.cadn.net.cn

您可以使用setAllowedHeaderValues方法:spring-doc.cadn.net.cn

允许某些用户代理
@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;
}
@Bean
fun httpFirewall(): StrictServerWebExchangeFirewall {
    val firewall = StrictServerWebExchangeFirewall()
    val allowed = Pattern.compile("[\\p{IsAssigned}&&[^\\p{IsControl}]]*")
    val userAgent = Pattern.compile(...)
    firewall.setAllowedHeaderValues { allowed.matcher(it).matches() || userAgent.matcher(it).matches() }
    return firewall
}

对于 Headers 值,您可以考虑在验证时将它们解析为 UTF-8:spring-doc.cadn.net.cn

将标头解析为 UTF-8
firewall.setAllowedHeaderValues((header) -> {
    String parsed = new String(header.getBytes(ISO_8859_1), UTF_8);
    return allowed.matcher(parsed).matches();
});
firewall.setAllowedHeaderValues {
    val parsed = String(header.getBytes(ISO_8859_1), UTF_8)
    return allowed.matcher(parsed).matches()
}

ServerExchangeRejectedHandlerinterface 用于处理ServerExchangeRejectedException由 Spring Security 的 throwServerWebExchangeFirewall. 默认情况下HttpStatusExchangeRejectedHandler用于在请求被拒绝时向客户端发送 HTTP 400 响应。 要自定义行为,用户可以公开ServerExchangeRejectedHandler豆。 例如,当请求被拒绝时,以下将发送 HTTP 404:spring-doc.cadn.net.cn

发送 404 请求被拒绝
@Bean
ServerExchangeRejectedHandler rejectedHandler() {
	return new HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND);
}
@Bean
fun rejectedHandler(): ServerExchangeRejectedHandler {
    return HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND)
}

可以通过创建自定义ServerExchangeRejectedHandler实现。spring-doc.cadn.net.cn