HTTP 访问限制
1. 前言
我们知道 Web 容器的实现有很多种,比如 Tomcat,WebSphere,WebLogic,JBoss 等,不同的 Web 容器在处理 URL 的规则上会有所不同,例如有些容器没有上下文的概念,有些容器不支持路径参数等。为了化解这些差异性,Spring Security 提供了对请求的过滤筛选机制。
Spring Security 的部分模块,会根据我们所定义的模式,来检查接收到的请求,以此决定该请求是否需要被处理。例如 FilterChainProxy
决定请求可以通过哪些过滤器链,FilterSecurityInterecptor
请求在触发哪些安全约束时会发生允许或拒绝的情况。所以对于开发者来说,理解其机制并且了解哪些 URL 适配哪些自定义模式就显得尤为重要。
本节主要讨论 Spring Security 中请求的筛选机制。
2. 关于 HTTP 防火墙
Servlet 规范中已经为 HttpServletRequest
定义了一些属性,这些属性通过 Getter 方法访问,并用作匹配处理。这些属性包括:contextPath
、servletPath
、pathInfo
和 queryString
。
Spring Security 仅关心应用程序的路径部分,并不关心 contextPath
。另一方面,在 Servlet 的规范中,缺少对 servletPath
和 pathInfo
的规定,比如 URL 中每个路径段都可能包含参数,然而这些参数是否应该算作 servletPath
或者 pathInfo
值中,规范却没有明确说明,并且在不同的 Servlet 容器中,其处理行为也不尽相同。当应用程序被部署在不从路径中解析参数的容器中时,攻击者可能将路径参数添加到请求的 URL 中,从而导致模式匹配的成功或者失败。还有另一种情况,路径中可能包含一些如遍历 /../
或者多个连续正斜杠 //
此类的内容,这也可能导致模式匹配的失效。有的容器在执行 Servlet 映射之前对其做了规范化处理,但不是所有容器都是。默认情况下,这些容器会自动拒绝未规范化的请求,并删除路径参数和重复斜杠。所以,为了保证程序在不同环境的一致性,我们就需要使用 FilterChainProxy
来管理安全过滤器链。还要注意一点,servletPath
和 pathInfo
是由容器解析得出的,因此我们还要避免使用分号。
路径的默认匹配策略使用了 Ant 风格,这也是最为常用的一种匹配模式。这个策略是由类 AntPathRequestMatcher
实现的,在 Spring 中由 AntPathMatcher
负责对 servletPath
和 pathInfo
属性执行不区分大小写的模式匹配,此过程中不处理 queryString。
有时候,我们会需要更复杂的匹配策略,比如正则表达式,这时候就需要用到 RegexRequestMatcher
对象了。
URL 匹配并不适合作为访问控制的唯一策略,我们还需要在服务层使用方法安全性来确保其安全性。由于 URL 是富于变化的,所以我们很难涵盖所有情况,最好的办法是采用白名单方式,只允许确认可用的地址被访问。
3. Spring Security 实现
在 Spring Security 项目中,默认使用 StrictHttpFirewall
对象,该对象对一些疑似恶意攻击的请求也进行了拒绝处理。假如该对象对我们的项目来说过于严格,那我们可以通过配置的方式定制哪些请求需要被拒绝,当然相应的,我们的应用程序也更容易受到攻击。
我们可以才用以下方式变更配置,如允许分号:
@Bean
public StrictHttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowSemicolon(true);
return firewall;
}
StrictHttpFirewall
对象提供了一个允许被跨域访问的 HTTP 方法列表,默认允许的方法有:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
如果希望修改此项默认策略,我们可以通过自定义 StrictHttpFirewall
对象实现。
例如,仅允许 GET
和 POST
方法:
@Bean
public StrictHttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST"));
return firewall;
}
也可以通过如下方法禁用所有方法的验证功能:
StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true)
4. 小结
本节讨论了 Spring Security 对请求的过滤和筛选处理,主要内容如下:
- HttpFirewall 接口是不同容器在没有统一规范的情况下,实现了对请求地址的一致化处理结果;
- HttpFirewall 的默认应用对象是
StrictHttpFirewall
,该对象采用了严格模式,对所有可疑请求都会进行拒绝处理; - Spring Security 提供了针对
StrictHttpFirewall
的配置入口,我们可以针对应用的安全需求定制化地址匹配方案。
下节讨论 Spring Security 中的加密方法。