知識庫 / Spring / Spring Security RSS 訂閱

Spring Security – 請求被拒絕異常

Spring Security
HongKong
7
12:28 PM · Dec 06 ,2025

1. 簡介

Spring Framework 5.0 到 5.0.4 版本、4.3 到 4.3.14 版本以及其他較早版本,在 Windows 系統上存在目錄遍歷安全漏洞。

靜態資源配置不當允許惡意用户訪問服務器的文件系統。例如,使用 file: 協議提供靜態資源會導致在 Windows 系統上非法訪問文件系統。

Spring Framework 承認了該漏洞 該漏洞 並於後續版本中進行了修復。

因此,此修復程序能夠保護應用程序免受目錄遍歷攻擊的影響。然而,與此修復程序一起,一些舊的 URL 現在會拋出 org.springframework.security.web.firewall.RequestRejectedException 異常

最後,在本教程中,讓我們學習關於 org.springframework.security.web.firewall.RequestRejectedExceptionStrictHttpFirewall 在目錄遍歷攻擊背景下的使用

2. 路徑遍歷漏洞

路徑遍歷或目錄遍歷漏洞允許非法訪問位於 Web 文檔根目錄之外的文件。例如,通過操縱 URL 可以未經授權地訪問不在文檔根目錄之外的文件。

儘管最新的和流行的 Web 服務器已經對這些攻擊進行了緩解,但攻擊者仍然可以使用 URL 編碼特殊字符(如“./”、“../”)來規避 Web 服務器的安全措施並獲得非法訪問權限。

此外,OWASP 討論了路徑遍歷漏洞及其應對方法。

3. Spring 框架漏洞

現在,讓我們嘗試複製這個漏洞,在學習如何修復它之前。

首先,讓我們克隆 Spring Framework MVC 示例。稍後,我們將修改 pom.xml 文件,並將現有 Spring Framework 版本替換為具有漏洞的版本。

克隆倉庫:

git clone [email protected]:spring-projects/spring-mvc-showcase.git

在克隆的目錄中,編輯 pom.xml 以將 Spring Framework 版本設置為 5.0.0.RELEASE

<org.springframework-version>5.0.0.RELEASE</org.springframework-version>

接下來,編輯 Web 配置類 WebMvcConfig,並修改 addResourceHandlers 方法,使用 file: 映射資源到本地文件目錄。

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry
      .addResourceHandler("/resources/**")
      .addResourceLocations("file:./src/", "/resources/");
}

稍後,構建制品並運行我們的 Web 應用:

mvn jetty:run

現在,當服務器啓動時,請調用以下 URL:

curl 'http://localhost:8080/spring-mvc-showcase/resources/%255c%255c%252e%252e%255c/%252e%252e%255c/%252e%252e%255c/%252e%252e%255c/%252e%252e%255c/windows/system.ini'

. 是 ....\ 的雙編碼形式,.\\. 的雙編碼形式。

危險地,響應將是 Windows 系統文件system.ini的內容。

4. Spring Security HttpFirewall 接口

Servlet 規範並未精確定義 servletPathpathInfo 的區別。因此,Servlet 容器在對這些值的翻譯上存在不一致性。

例如,在 Tomcat 9 上,對於 URL http://localhost:8080/api/v1/users/1,URI /1 被認為是路徑變量。

另一方面,以下代碼返回 /api/v1/users/1

request.getServletPath()

然而,下面的命令返回一個null

request.getPathInfo()

無法從 URI 中區分路徑變量可能導致路徑遍歷/目錄遍歷等潛在攻擊。例如,用户可以通過在 URL 中包含 \\,  /../, . 來利用服務器上的系統文件。不幸的是,只有少數 Servlet 容器會規範化這些 URL。

Spring Security 能夠解決這些問題。Spring Security 在不同容器中表現一致,並利用 HttpFirewall 接口規範化這些惡意 URL。該接口有以下兩種實現:

4.1. DefaultHttpFirewall

首先,不要被實現類的名稱所迷惑。換句話説,這並不是默認的 HttpFirewall 實現。

防火牆嘗試對 URL 進行清理或規範化,並對 servletPathpathInfo 在容器之間進行標準化。 此外,我們可以通過顯式聲明一個 @Bean 來覆蓋默認的 HttpFirewall 行為。

@Bean
public HttpFirewall getHttpFirewall() {
    return new DefaultHttpFirewall();
}

然而,<em >StrictHttpFirewall</em> 提供了一個健壯且安全的實現,並且是推薦的實現。

4.2. StrictHttpFirewall

StrictHttpFirewall** 是 HttpFirewall 的默認且更嚴格的實現。與 DefaultHttpFirewall 不同,StrictHttpFirewall** 會拒絕任何未進行規範化的 URL,從而提供更嚴格的保護。此外,該實現還能夠保護應用程序免受諸如 跨站跟蹤 (XST)HTTP 動詞篡改 等攻擊。

此外,該實現具有可定製性,並且具有合理的默認值。換句話説,我們可以禁用(但不建議)某些功能,例如允許分號作為 URI 的一部分。

@Bean
public HttpFirewall getHttpFirewall() {
    StrictHttpFirewall strictHttpFirewall = new StrictHttpFirewall();
    strictHttpFirewall.setAllowSemicolon(true);
    return strictHttpFirewall;
}

簡而言之,StrictHttpFirewall 會通過拋出 org.springframework.security.web.firewall.RequestRejectedException 來拒絕可疑請求。

最後,我們使用 Spring REST 和 Spring Security 對用户進行 CRUD 操作,開發一個用户管理應用程序,並觀察到 StrictHttpFirewall 的應用。

5. 依賴項

聲明以下依賴項:Spring SecuritySpring Web 依賴項:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    <version>3.1.5</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>3.1.5</version>
</dependency>

6. Spring Security 配置

接下來,讓我們使用 Basic 身份驗證來安全地配置我們的應用程序,通過創建一個配置類來創建 SecurityFilterChain 託管 Bean:

@Configuration
public class HttpFirewallConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(authorizationManagerRequestMatcherRegistry ->
                    authorizationManagerRequestMatcherRegistry.requestMatchers("/error").permitAll().anyRequest().authenticated())
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}

默認情況下,Spring Security 提供一個默認密碼,該密碼會在每次重啓時更改。因此,讓我們在 application.properties 中創建一個默認用户名和密碼:

spring.security.user.name=user
spring.security.user.password=password

由此可見,我們將會使用這些憑據來訪問我們的安全REST API。

7. 構建安全的 REST API

現在,讓我們構建我們的用户管理 REST API:

@PostMapping
public ResponseEntity<Response> createUser(@RequestBody User user) {
    userService.saveUser(user);
    Response response = new Response()
      .withTimestamp(System.currentTimeMillis())
      .withCode(HttpStatus.CREATED.value())
      .withMessage("User created successfully");
    URI location = URI.create("/users/" + user.getId());
    return ResponseEntity.created(location).body(response);
}
 
@DeleteMapping("/{userId}")
public ResponseEntity<Response> deleteUser(@PathVariable("userId") String userId) {
    userService.deleteUser(userId);
    return ResponseEntity.ok(new Response(200,
      "The user has been deleted successfully", System.currentTimeMillis()));
}

現在,讓我們構建並運行該應用程序:

mvn spring-boot:run

8. 測試 API

現在,讓我們通過 cURL 創建一個 User

curl -i --user user:password -d @request.json -H "Content-Type: application/json" 
     -H "Accept: application/json" http://localhost:8080/api/v1/users

這是個 request.json

{
    "id":"1",
    "username":"navuluri",
    "email":"[email protected]"
}

Consequently, the response is:

HTTP/1.1 201
Location: /users/1
Content-Type: application/json
{
  "code":201,
  "message":"User created successfully",
  "timestamp":1632808055618
}

現在,讓我們配置我們的 StrictHttpFirewall 以拒絕所有 HTTP 方法的請求:

@Bean
public HttpFirewall configureFirewall() {
    StrictHttpFirewall strictHttpFirewall = new StrictHttpFirewall();
    strictHttpFirewall
      .setAllowedHttpMethods(Collections.emptyList());
    return strictHttpFirewall;
}

接下來,讓我們再次調用該API。由於我們配置了 StrictHttpFirewall 以限制所有HTTP方法,因此這次我們得到了一個錯誤。

在日誌中,我們有以下異常:

org.springframework.security.web.firewall.RequestRejectedException: 
The request was rejected because the HTTP method "POST" was not included
  within the list of allowed HTTP methods []

自 Spring Security v6.1.5 版本起,我們可以使用 RequestRejectedHandler 來自定義在出現 RequestRejectedException 時產生的 HTTP 狀態碼

@Bean
public RequestRejectedHandler requestRejectedHandler() {
   return new HttpStatusRequestRejectedHandler();
}

請注意,使用 HttpStatusRequestRejectedHandler 時,默認的 HTTP 狀態碼是 400。但是,我們可以通過在 HttpStatusRequestRejectedHandler 類的構造函數中傳遞狀態碼來自定義它。

現在,讓我們重新配置 StrictHttpFirewall 以允許在 URL 中使用 \\ ,以及在 HTTP GETPOSTDELETEOPTIONS 方法中。

strictHttpFirewall.setAllowBackSlash(true);
strictHttpFirewall.setAllowedHttpMethods(Arrays.asList("GET","POST","DELETE", "OPTIONS")

接下來,調用API:

curl -i --user user:password -d @request.json -H "Content-Type: application/json" 
     -H "Accept: application/json" http://localhost:8080/api\\v1/users

以下是響應:

{
  "code":201,
  "message":"User created successfully",
  "timestamp":1632812660569
}

最後,我們回到 StrictHttpFirewall 的原始嚴格功能,通過刪除 @Bean 聲明來實現。

接下來,讓我們嘗試使用可疑的 URL 調用的我們的 API:

curl -i --user user:password -d @request.json -H "Content-Type: application/json" 
      -H "Accept: application/json" http://localhost:8080/api/v1//users
curl -i --user user:password -d @request.json -H "Content-Type: application/json" 
      -H "Accept: application/json" http://localhost:8080/api/v1\\users

立即,以上所有請求均以錯誤日誌失敗:

org.springframework.security.web.firewall.RequestRejectedException: 
The request was rejected because the URL contained a potentially malicious String "//"

9. 結論

本文介紹了 Spring Security 防禦惡意 URL 的機制,這些 URL 可能導致路徑遍歷/目錄遍歷攻擊。

DefaultHttpFirewall 嘗試規範化惡意 URL。然而,StrictHttpFirewall 會通過拋出 RequestRejectedException 來拒絕這些請求。 StrictHttpFirewall 不僅保護我們免受路徑遍歷攻擊,還保護我們免受其他攻擊。 因此,強烈建議同時使用 StrictHttpFirewall 及其默認配置。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.