对于最新的稳定版本,请使用 Spring Security 6.4.1! |
ServerWebExchange防火墙
恶意用户可以通过多种方式创建请求,从而利用应用程序。
Spring Security 提供了允许拒绝看似恶意的请求。
默认实现是拒绝恶意请求。ServerWebExchangeFirewall
StrictServerWebExchangeFirewall
例如,请求可能包含路径遍历序列(例如 )或多个正斜杠 (),这也可能导致模式匹配失败。
一些容器在执行 servlet 映射之前会规范化这些内容,但其他容器则不会。
为了防止此类问题,请使用策略来检查和包装请求。
默认情况下,会自动拒绝未规范化的请求,并删除路径参数以进行匹配。
(因此,例如,原始请求路径 of 将返回为 .)
因此,必须使用 a。/../
//
WebFilterChainProxy
ServerWebExchangeFirewall
/secure;hack=1/somefile.html;hack=2
/secure/somefile.html
WebFilterChainProxy
在实践中,我们建议您在服务层使用方法安全性来控制对应用程序的访问,而不是完全依赖于在 Web 应用程序级别定义的安全约束的使用。
URL 会发生变化,因此很难考虑应用程序可能支持的所有可能的 URL 以及可能如何处理请求。
您应该限制自己使用一些易于理解的简单模式。
始终尝试使用 “deny-by-default” 方法,其中最后定义一个 catch-all 通配符 ( 或 ) 来拒绝访问。
/
在服务层定义的安全性更加健壮且更难绕过,因此您应该始终利用 Spring Security 的方法安全选项。
您可以通过将其公开为 Bean 来自定义 。ServerWebExchangeFirewall
-
Java
-
Kotlin
@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 动词篡改,提供了允许的有效 HTTP 方法的允许列表。
默认的有效方法是 、 和 。
如果您的应用程序需要修改有效的方法,您可以配置自定义 Bean。
以下示例仅允许 HTTP 和方法:StrictServerWebExchangeFirewall
DELETE
GET
HEAD
OPTIONS
PATCH
POST
PUT
StrictServerWebExchangeFirewall
GET
POST
-
Java
-
Kotlin
@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 方法(不推荐),则可以使用 .
这样做会完全禁用 HTTP 方法的验证。StrictServerWebExchangeFirewall.setUnsafeAllowAnyHttpMethod(true)
StrictServerWebExchangeFirewall
此外,还会检查标头名称和值以及参数名称。
它要求每个字符都有一个定义的码位,而不是控制字符。
该要求可根据需要通过以下方法放宽或调整:
-
StrictServerWebExchangeFirewall#setAllowedHeaderNames(Predicate)
-
StrictServerWebExchangeFirewall#setAllowedHeaderValues(Predicate)
-
StrictServerWebExchangeFirewall#setAllowedParameterNames(Predicate)
参数值也可以使用 来控制。 |
例如,要关闭此检查,您可以将 instance 与 always return :StrictServerWebExchangeFirewall
Predicate
true
-
Java
-
Kotlin
@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
}
或者,可能需要允许特定值。
例如,iPhone Xʀ 使用的 a 包含不在 ISO-8859-1 字符集中的字符。
由于这个事实,一些应用程序服务器将此值解析为两个单独的字符,后者是未定义的字符。User-Agent
您可以使用以下方法解决此问题:setAllowedHeaderValues
-
Java
-
Kotlin
@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:
-
Java
-
Kotlin
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()
}
该接口用于处理 Spring Security 的 throw 。
默认情况下,用于在请求被拒绝时向客户端发送 HTTP 400 响应。
要自定义行为,用户可以公开一个 Bean。
例如,当请求被拒绝时,以下将发送 HTTP 404:ServerExchangeRejectedHandler
ServerExchangeRejectedException
ServerWebExchangeFirewall
HttpStatusExchangeRejectedHandler
ServerExchangeRejectedHandler
-
Java
-
Kotlin
@Bean
ServerExchangeRejectedHandler rejectedHandler() {
return new HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND);
}
@Bean
fun rejectedHandler(): ServerExchangeRejectedHandler {
return HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND)
}
可以通过创建自定义实现来完全自定义处理。ServerExchangeRejectedHandler