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