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

服务器Web交换防火墙

恶意用户可以通过多种方式创建请求,这些请求可能会利用应用程序中的漏洞。 Spring Security 提供了 ServerWebExchangeFirewall,以允许拒绝看起来像恶意的请求。 默认实现是 StrictServerWebExchangeFirewall,它会拒绝恶意请求。spring-doc.cadn.net.cn

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

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

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

您可以通过将其暴露为一个Bean来自定义ServerWebExchangeFirewallspring-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 方法白名单。 默认的有效方法是 DELETEGETHEADOPTIONSPATCHPOSTPUT。 如果您的应用程序需要修改有效方法,可以配置自定义的 StrictServerWebExchangeFirewall Bean。 以下示例仅允许 HTTP GETPOST 方法: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

例如,要关闭此检查,您可以将StrictServerWebExchangeFirewall与总是返回Predicatetrue实例进行连接: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
}

对于头部(header)值,您可以考虑在验证时将其解析为 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()
}

ServerExchangeRejectedHandler 接口用于处理由 Spring Security 的 ServerExchangeRejectedException 抛出的 ServerWebExchangeFirewall。 默认情况下,HttpStatusExchangeRejectedHandler 会在请求被拒绝时向客户端发送 HTTP 400 响应。 为了自定义行为,用户可以暴露一个 ServerExchangeRejectedHandler Bean。例如,以下代码将在请求被拒绝时发送 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