对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

对于最新的稳定版本,请使用 Spring Security 6.3.1Spring中文文档

了解机制是什么以及在针对您定义的模式进行测试时使用的 URL 值非常重要。Spring中文文档

servlet 规范定义了几个属性,这些属性可以通过 getter 方法访问,我们可能想要与之匹配。 它们是 、 、 和 。 Spring Security 只对保护应用程序内的路径感兴趣,因此会忽略。 遗憾的是,servlet 规范没有准确定义特定请求 URI 的值和包含的内容。 例如,URL 的每个路径段都可能包含 RFC 2396 中定义的参数(当浏览器不支持 cookie 并且参数附加到 URL 的分号后时,您可能已经看到了这一点。 但是,RFC 允许在 URL 的任何路径段中存在这些参数。 该规范没有明确说明这些是否应该包含在 and 值中,并且行为在不同的 servlet 容器之间会有所不同。 存在一种危险,即当应用程序部署在不从这些值中剥离路径参数的容器中时,攻击者可能会将它们添加到请求的 URL 中,从而导致模式匹配意外成功或失败。 (一旦请求离开 ,将返回原始值,因此应用程序仍可用。 传入 URL 中的其他变体也是可能的。 例如,它可能包含路径遍历序列(如 )或多个正斜杠 (),这也可能导致模式匹配失败。 一些容器在执行 servlet 映射之前会规范化这些内容,但其他容器则不会。 为了防止出现此类问题,请使用一种策略来检查和包装请求。 默认情况下,会自动拒绝非规范化的请求,并删除路径参数和重复斜杠以进行匹配。 (因此,例如,原始请求路径返回为 。 因此,必须使用 a 来管理安全筛选器链。 请注意,和值由容器解码,因此应用程序不应包含任何包含分号的有效路径,因为这些部分会被删除以进行匹配。HttpServletRequestcontextPathservletPathpathInfoqueryStringcontextPathservletPathpathInfojsessionidservletPathpathInfoFilterChainProxy/..///FilterChainProxyHttpFirewall/secure;hack=1/somefile.html;hack=2/secure/somefile.htmlFilterChainProxyservletPathpathInfoSpring中文文档

如前所述,默认策略是使用 Ant 样式的路径进行匹配,这可能是大多数用户的最佳选择。 该策略在类中实现,该类使用 Spring 的模式与连接的 和 执行不区分大小写的匹配,忽略 。AntPathRequestMatcherAntPathMatcherservletPathpathInfoqueryStringSpring中文文档

如果您需要更强大的匹配策略,则可以使用正则表达式。 然后,策略实施是 。 有关更多信息,请参阅此类的 JavadocRegexRequestMatcherSpring中文文档

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

在服务层定义的安全性更加强大,也更难绕过,因此您应该始终利用 Spring Security 的方法安全选项。Spring中文文档

它还通过拒绝 HTTP 响应标头中的换行符来防止 HTTP 响应拆分HttpFirewallSpring中文文档

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

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

为了防止跨站点跟踪 (XST)HTTP 谓词篡改,提供了允许的有效 HTTP 方法的允许列表。 默认有效方法为 、 、 、 、 和 。 如果您的应用程序需要修改有效方法,您可以配置自定义 Bean。 以下示例仅允许 HTTP 和方法:StrictHttpFirewallDELETEGETHEADOPTIONSPATCHPOSTPUTStrictHttpFirewallGETPOSTSpring中文文档

只允许 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,POST"/>

<http-firewall ref="httpFirewall"/>
@Bean
fun httpFirewall(): StrictHttpFirewall {
    val firewall = StrictHttpFirewall()
    firewall.setAllowedHttpMethods(listOf("GET", "POST"))
    return firewall
}

如果使用 ,则当前会创建一个 HTTP 方法作为空 String()。 这是一个无效的 HTTP 方法,被 Spring Security 拒绝。 您可以通过将其替换为 来解决此问题。 请参阅SPR_16851,了解请求改进此问题的问题。new MockHttpServletRequest()""new MockHttpServletRequest("GET", "")Spring中文文档

如果使用 ,则当前会创建一个 HTTP 方法作为空 String()。 这是一个无效的 HTTP 方法,被 Spring Security 拒绝。 您可以通过将其替换为 来解决此问题。 请参阅SPR_16851,了解请求改进此问题的问题。new MockHttpServletRequest()""new MockHttpServletRequest("GET", "")Spring中文文档

如果必须允许任何 HTTP 方法(不推荐),则可以使用 . 这样做会完全禁用对 HTTP 方法的验证。StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true)Spring中文文档

StrictHttpFirewall还检查标头名称和值以及参数名称。 它要求每个字符都有一个定义的代码点,而不是控制字符。Spring中文文档

此要求可根据需要使用以下方法放宽或调整:Spring中文文档

参数值也可以用 来控制。setAllowedParameterValues(Predicate)Spring中文文档

参数值也可以用 来控制。setAllowedParameterValues(Predicate)Spring中文文档

例如,要关闭此检查,您可以将始终返回的实例连接起来:StrictHttpFirewallPredicatetrueSpring中文文档

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

例如,iPhone Xʀ 使用的字符不在 ISO-8859-1 字符集中。 因此,某些应用程序服务器将此值解析为两个单独的字符,后者是未定义的字符。User-AgentSpring中文文档

您可以使用以下方法解决此问题:setAllowedHeaderValuesSpring中文文档

允许某些用户代理
@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中文文档

将标头解析为 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()
}