HTTP 訪問限制
1. 前言
我們知道 Web 容器的實現(xiàn)有很多種,比如 Tomcat,WebSphere,WebLogic,JBoss 等,不同的 Web 容器在處理 URL 的規(guī)則上會有所不同,例如有些容器沒有上下文的概念,有些容器不支持路徑參數(shù)等。為了化解這些差異性,Spring Security 提供了對請求的過濾篩選機制。
Spring Security 的部分模塊,會根據(jù)我們所定義的模式,來檢查接收到的請求,以此決定該請求是否需要被處理。例如 FilterChainProxy
決定請求可以通過哪些過濾器鏈,FilterSecurityInterecptor
請求在觸發(fā)哪些安全約束時會發(fā)生允許或拒絕的情況。所以對于開發(fā)者來說,理解其機制并且了解哪些 URL 適配哪些自定義模式就顯得尤為重要。
本節(jié)主要討論 Spring Security 中請求的篩選機制。
2. 關(guān)于 HTTP 防火墻
Servlet 規(guī)范中已經(jīng)為 HttpServletRequest
定義了一些屬性,這些屬性通過 Getter 方法訪問,并用作匹配處理。這些屬性包括:contextPath
、servletPath
、pathInfo
和 queryString
。
Spring Security 僅關(guān)心應用程序的路徑部分,并不關(guān)心 contextPath
。另一方面,在 Servlet 的規(guī)范中,缺少對 servletPath
和 pathInfo
的規(guī)定,比如 URL 中每個路徑段都可能包含參數(shù),然而這些參數(shù)是否應該算作 servletPath
或者 pathInfo
值中,規(guī)范卻沒有明確說明,并且在不同的 Servlet 容器中,其處理行為也不盡相同。當應用程序被部署在不從路徑中解析參數(shù)的容器中時,攻擊者可能將路徑參數(shù)添加到請求的 URL 中,從而導致模式匹配的成功或者失敗。還有另一種情況,路徑中可能包含一些如遍歷 /../
或者多個連續(xù)正斜杠 //
此類的內(nèi)容,這也可能導致模式匹配的失效。有的容器在執(zhí)行 Servlet 映射之前對其做了規(guī)范化處理,但不是所有容器都是。默認情況下,這些容器會自動拒絕未規(guī)范化的請求,并刪除路徑參數(shù)和重復斜杠。所以,為了保證程序在不同環(huán)境的一致性,我們就需要使用 FilterChainProxy
來管理安全過濾器鏈。還要注意一點,servletPath
和 pathInfo
是由容器解析得出的,因此我們還要避免使用分號。
路徑的默認匹配策略使用了 Ant 風格,這也是最為常用的一種匹配模式。這個策略是由類 AntPathRequestMatcher
實現(xiàn)的,在 Spring 中由 AntPathMatcher
負責對 servletPath
和 pathInfo
屬性執(zhí)行不區(qū)分大小寫的模式匹配,此過程中不處理 queryString。
有時候,我們會需要更復雜的匹配策略,比如正則表達式,這時候就需要用到 RegexRequestMatcher
對象了。
URL 匹配并不適合作為訪問控制的唯一策略,我們還需要在服務層使用方法安全性來確保其安全性。由于 URL 是富于變化的,所以我們很難涵蓋所有情況,最好的辦法是采用白名單方式,只允許確認可用的地址被訪問。
3. Spring Security 實現(xiàn)
在 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
對象實現(xiàn)。
例如,僅允許 GET
和 POST
方法:
@Bean
public StrictHttpFirewall httpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowedHttpMethods(Arrays.asList("GET", "POST"));
return firewall;
}
也可以通過如下方法禁用所有方法的驗證功能:
StrictHttpFirewall.setUnsafeAllowAnyHttpMethod(true)
4. 小結(jié)
本節(jié)討論了 Spring Security 對請求的過濾和篩選處理,主要內(nèi)容如下:
- HttpFirewall 接口是不同容器在沒有統(tǒng)一規(guī)范的情況下,實現(xiàn)了對請求地址的一致化處理結(jié)果;
- HttpFirewall 的默認應用對象是
StrictHttpFirewall
,該對象采用了嚴格模式,對所有可疑請求都會進行拒絕處理; - Spring Security 提供了針對
StrictHttpFirewall
的配置入口,我們可以針對應用的安全需求定制化地址匹配方案。
下節(jié)討論 Spring Security 中的加密方法。