对于最新的稳定版本,请使用 Spring Security 6.3.1! |
对于最新的稳定版本,请使用 Spring Security 6.3.1! |
请务必了解机制是什么,以及在针对您定义的模式进行测试时使用的 URL 值。
Servlet 规范定义了几个属性,这些属性可以通过 getter 方法访问,我们可能希望与这些属性进行匹配。
它们是 、 和 。
Spring Security 只对保护应用程序内的路径感兴趣,因此会忽略。
遗憾的是,servlet 规范没有准确定义特定请求 URI 的值和将包含的内容。
例如,URL 的每个路径段都可能包含 RFC 2396 中定义的参数HttpServletRequest
contextPath
servletPath
pathInfo
queryString
contextPath
servletPath
pathInfo
[1].
该规范没有明确说明这些是否应该包含在 and 值中,并且行为在不同的 servlet 容器之间会有所不同。
存在一种危险,即当应用程序部署在不从这些值中剥离路径参数的容器中时,攻击者可能会将它们添加到请求的 URL 中,从而导致模式匹配意外成功或失败。servletPath
pathInfo
[2].
传入 URL 中的其他变体也是可能的。
例如,它可能包含路径遍历序列(如 )或多个正斜杠 (),这也可能导致模式匹配失败。
一些容器在执行 servlet 映射之前会规范化这些内容,但其他容器则不会。
为了防止出现此类问题,请使用一种策略来检查和包装请求。
默认情况下,会自动拒绝未规范化的请求,并删除路径参数和重复斜杠以进行匹配。/../
//
FilterChainProxy
HttpFirewall
[3].
因此,必须使用 a 来管理安全筛选器链。
请注意,和值由容器解码,因此应用程序不应包含任何包含分号的有效路径,因为这些部分将被删除以进行匹配。FilterChainProxy
servletPath
pathInfo
如上所述,默认策略是使用 Ant 样式的路径进行匹配,这可能是大多数用户的最佳选择。
该策略在类中实现,该类使用 Spring 的模式与连接的 和 执行不区分大小写的匹配,忽略 .AntPathRequestMatcher
AntPathMatcher
servletPath
pathInfo
queryString
如果出于某种原因,您需要更强大的匹配策略,则可以使用正则表达式。
然后,策略实施是 。
有关更多信息,请参阅此类的 Javadoc。RegexRequestMatcher
在实践中,我们建议您在服务层使用方法安全性来控制对应用程序的访问,并且不要完全依赖于在 Web 应用程序级别定义的安全约束的使用。 URL 会发生变化,因此很难考虑应用程序可能支持的所有可能的 URL 以及请求的处理方式。 您应该尝试将自己限制为使用一些易于理解的简单蚂蚁路径。 始终尝试使用“默认拒绝”方法,其中最后定义了一个包含所有通配符(/或)并拒绝访问。
在服务层定义的安全性更加强大,也更难绕过,因此您应该始终利用 Spring Security 的方法安全选项。
它还通过拒绝 HTTP 响应标头中的换行符来防止 HTTP 响应拆分。HttpFirewall
默认情况下,使用 。
此实现拒绝看似恶意的请求。
如果它对您的需求来说太严格了,那么您可以自定义拒绝哪些类型的请求。
但是,请务必知道这可能会使应用程序受到攻击。
例如,如果您希望利用 Spring MVC 的矩阵变量,可以使用以下配置:StrictHttpFirewall
-
Java
-
XML
-
Kotlin
@Bean
public StrictHttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowSemicolon(true);
return firewall;
}
<b:bean id="httpFirewall"
class="org.springframework.security.web.firewall.StrictHttpFirewall"
p:allowSemicolon="true"/>
<http-firewall ref="httpFirewall"/>
@Bean
fun httpFirewall(): StrictHttpFirewall {
val firewall = StrictHttpFirewall()
firewall.setAllowSemicolon(true)
return firewall
}
它提供了有效 HTTP 方法的允许列表,这些方法允许防止跨站点跟踪 (XST) 和 HTTP 谓词篡改。
默认有效方法为“DELETE”、“GET”、“HEAD”、“OPTIONS”、“PATCH”、“POST”和“PUT”。
如果您的应用程序需要修改有效方法,您可以配置自定义 Bean。
例如,以下方法仅允许 HTTP“GET”和“POST”方法:StrictHttpFirewall
StrictHttpFirewall
-
Java
-
XML
-
Kotlin
@Bean
public StrictHttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST"));
return firewall;
}
<b:bean id="httpFirewall"
class="org.springframework.security.web.firewall.StrictHttpFirewall"
p:allowedHttpMethods="GET,HEAD"/>
<http-firewall ref="httpFirewall"/>
@Bean
fun httpFirewall(): StrictHttpFirewall {
val firewall = StrictHttpFirewall()
firewall.setAllowedHttpMethods(listOf("GET", "POST"))
return firewall
}
如果您正在使用它,则当前会创建一个 HTTP 方法作为空字符串 “”。
这是一个无效的 HTTP 方法,将被 Spring Security 拒绝。
您可以通过将其替换为 来解决此问题。
请参阅SPR_16851,了解请求改进此问题的问题。 |
如果您正在使用它,则当前会创建一个 HTTP 方法作为空字符串 “”。
这是一个无效的 HTTP 方法,将被 Spring Security 拒绝。
您可以通过将其替换为 来解决此问题。
请参阅SPR_16851,了解请求改进此问题的问题。 |
如果必须允许任何 HTTP 方法(不推荐),则可以使用 .
这将完全禁用 HTTP 方法的验证。StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true)
StrictHttpFirewall
还检查标头名称和值以及参数名称。
它要求每个字符都有一个定义的代码点,而不是控制字符。
可以使用以下方法根据需要放宽或调整此要求:
-
StrictHttpFirewall#setAllowedHeaderNames(Predicate)
-
StrictHttpFirewall#setAllowedHeaderValues(Predicate)
-
StrictHttpFirewall#setAllowedParameterNames(Predicate)
此外,参数值可以使用 来控制。setAllowedParameterValues(Predicate) |
此外,参数值可以使用 来控制。setAllowedParameterValues(Predicate) |
例如,要关闭此检查,您可以将 your 与始终返回的 s 连接,如下所示:StrictHttpFirewall
Predicate
true
-
Java
-
Kotlin
@Bean
public StrictHttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowedHeaderNames((header) -> true);
firewall.setAllowedHeaderValues((header) -> true);
firewall.setAllowedParameterNames((parameter) -> true);
return firewall;
}
@Bean
fun httpFirewall(): StrictHttpFirewall {
val firewall = StrictHttpFirewall()
firewall.setAllowedHeaderNames { true }
firewall.setAllowedHeaderValues { true }
firewall.setAllowedParameterNames { true }
return firewall
}
或者,可能需要允许特定值。
例如,iPhone Xʀ 使用的字符包含不在 ISO-8859-1 字符集中的字符。
因此,某些应用程序服务器会将此值解析为两个单独的字符,后者是未定义的字符。User-Agent
您可以使用该方法解决此问题,如下所示:setAllowedHeaderValues
-
Java
-
Kotlin
@Bean
public StrictHttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
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(): StrictHttpFirewall {
val firewall = StrictHttpFirewall()
val allowed = Pattern.compile("[\\p{IsAssigned}&&[^\\p{IsControl}]]*")
val userAgent = Pattern.compile(...)
firewall.setAllowedHeaderValues { allowed.matcher(it).matches() || userAgent.matcher(it).matches() }
return firewall
}
对于标头值,您可以考虑在验证时将它们解析为 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()
}
jsessionid
FilterChainProxy