对于最新的稳定版本,请使用 Spring Security 6.5.3spring-doc.cadn.net.cn

Http防火墙

了解机制是什么以及根据您定义的模式进行测试时使用的 URL 值非常重要。spring-doc.cadn.net.cn

Servlet 规范为HttpServletRequest这些可以通过 getter 方法访问,并且我们可能想要与之匹配。 这些是contextPath,servletPath,pathInfoqueryString. Spring Security 只对保护应用程序中的路径感兴趣,因此contextPath被忽略。 不幸的是,servlet 规范并没有准确定义servletPathpathInfo将包含特定请求 URI。 例如,URL 的每个路径段都可能包含 RFC 2396 中定义的参数 [1]. 规范没有明确说明这些是否应该包含在servletPathpathInfo值和行为在不同的 servlet 容器之间有所不同。 存在一种危险,即当应用程序部署在未从这些值中剥离路径参数的容器中时,攻击者可能会将它们添加到请求的 URL 中,以导致模式匹配意外成功或失败。[2]. 传入 URL 的其他变体也是可能的。 例如,它可以包含路径遍历序列(例如/../) 或多个正斜杠 () 也可能导致模式匹配失败。 一些容器在执行 servlet 映射之前对这些进行规范化,但其他容器则不会。 为了防止此类问题,//FilterChainProxy使用HttpFirewall策略来检查和包装请求。 默认情况下,未规范化的请求会自动拒绝,并且路径参数和重复斜杠将被删除以进行匹配。[3]. 因此,必须FilterChainProxy用于管理安全过滤器链。 请注意,servletPathpathInfo值由容器解码,因此应用程序不应具有任何包含分号的有效路径,因为出于匹配目的,这些部分将被删除。spring-doc.cadn.net.cn

如上所述,默认策略是使用 Ant 样式的路径进行匹配,这可能是大多数用户的最佳选择。 该策略在类中实现AntPathRequestMatcher它使用 Spring 的AntPathMatcher对模式与串联的servletPathpathInfo,忽略queryString.spring-doc.cadn.net.cn

如果由于某种原因,您需要更强大的匹配策略,您可以使用正则表达式。 然后战略实施RegexRequestMatcher. 有关更多信息,请参阅此类的 Javadoc。spring-doc.cadn.net.cn

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

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

HttpFirewall还通过拒绝 HTTP 响应标头中的换行符来防止 HTTP 响应拆分spring-doc.cadn.net.cn

默认情况下,StrictHttpFirewall被使用。 此实现会拒绝看似恶意的请求。 如果它对您的需求来说太严格,那么您可以自定义拒绝哪些类型的请求。 但是,请务必知道这可能会使您的应用程序受到攻击。 例如,如果您希望利用 Spring MVC 的矩阵变量,可以使用以下配置:spring-doc.cadn.net.cn

允许矩阵变量
@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
}

StrictHttpFirewall提供了允许防止跨站点跟踪 (XST)HTTP 谓词篡改的有效 HTTP 方法的允许列表。 默认有效的方法是“DELETE”、“GET”、“HEAD”、“OPTIONS”、“PATCH”、“POST”和“PUT”。 如果您的应用程序需要修改有效方法,您可以配置自定义StrictHttpFirewall豆。 例如,以下内容将仅允许 HTTP“GET”和“POST”方法:spring-doc.cadn.net.cn

仅允许 GET 和 POST
@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
}

如果您正在使用new MockHttpServletRequest()它当前将 HTTP 方法创建为空字符串“”。 这是一个无效的HTTP方法,将被Spring Security拒绝。 您可以通过将其替换为new MockHttpServletRequest("GET", ""). 有关请求改进此问题的问题,请参阅SPR_16851spring-doc.cadn.net.cn

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

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

可以使用以下方法根据需要放宽或调整此要求:spring-doc.cadn.net.cn

此外,参数值可以通过以下方式控制setAllowedParameterValues(Predicate).

例如,要关闭此检查,您可以将StrictHttpFirewallPredicate总是返回的true这样:spring-doc.cadn.net.cn

允许任何标头名称、标头值和参数名称
@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
}

或者,可能需要允许特定值。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 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,如下所示: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()
}

1. 当浏览器不支持 cookie 并且jsessionid参数附加到 URL 的分号后。但是,RFC 允许在 URL 的任何路径段中存在这些参数
2. 请求离开FilterChainProxy,因此仍可用于应用程序。
3. 例如,原始请求路径/secure;hack=1/somefile.html;hack=2将作为/secure/somefile.html.