HttpFirewall 防火墙

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

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

如前所述,默认策略是使用 Ant 风格的路径进行匹配,这很可能是大多数用户的最佳选择。 该策略在 类 中实现,该类使用 Spring 的 来执行模式与连接的 和 的不区分大小写的匹配,而忽略 .AntPathRequestMatcherAntPathMatcherservletPathpathInfoqueryStringspring-doc.cn

如果您需要更强大的匹配策略,可以使用正则表达式。 然后,策略实施为 。 有关更多信息,请参见 RegexRequestMatcher Javadoc。RegexRequestMatcherspring-doc.cn

在实践中,我们建议您在服务层使用方法安全性来控制对应用程序的访问,而不是完全依赖于在 Web 应用程序级别定义的安全约束的使用。 URL 会发生变化,因此很难考虑应用程序可能支持的所有可能的 URL 以及可能如何处理请求。 您应该限制自己使用一些易于理解的简单 Ant 路径。 始终尝试使用 “deny-by-default” 方法,其中最后定义一个 catch-all 通配符 () 来拒绝访问。/spring-doc.cn

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

它还通过拒绝 HTTP 响应标头中的新行字符来防止 HTTP 响应拆分HttpFirewallspring-doc.cn

默认情况下,使用 implementation 。 此实现拒绝看似恶意的请求。 如果它对您的需求来说太严格,您可以自定义拒绝的请求类型。 但是,请务必知道这可能会使您的应用程序受到攻击。 例如,如果你希望使用 Spring MVC 的矩阵变量,你可以使用以下配置:StrictHttpFirewallspring-doc.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
}

为了防止跨站点跟踪 (XST)HTTP 动词篡改,提供了允许的有效 HTTP 方法的允许列表。 默认的有效方法是 、 和 。 如果您的应用程序需要修改有效的方法,您可以配置自定义 Bean。 以下示例仅允许 HTTP 和方法:StrictHttpFirewallDELETEGETHEADOPTIONSPATCHPOSTPUTStrictHttpFirewallGETPOSTspring-doc.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,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-doc.cn

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

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

该要求可根据需要通过以下方法放宽或调整:spring-doc.cn

  • StrictHttpFirewall#setAllowedHeaderNames(Predicate)spring-doc.cn

  • StrictHttpFirewall#setAllowedHeaderValues(Predicate)spring-doc.cn

  • StrictHttpFirewall#setAllowedParameterNames(Predicate)spring-doc.cn

参数值也可以使用 来控制。setAllowedParameterValues(Predicate)spring-doc.cn

例如,要关闭此检查,您可以将 instance 与 always return :StrictHttpFirewallPredicatetruespring-doc.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.cn

例如,iPhone Xʀ 使用的 a 包含不在 ISO-8859-1 字符集中的字符。 由于这个事实,一些应用程序服务器将此值解析为两个单独的字符,后者是未定义的字符。User-Agentspring-doc.cn

您可以使用以下方法解决此问题:setAllowedHeaderValuesspring-doc.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
}

对于 Headers 值,您可以考虑在验证时将它们解析为 UTF-8:spring-doc.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()
}