Spring Security – 請求被拒絕異常

Spring Security
Remote
1
08:10 AM · Nov 30 ,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 Framework 漏洞

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

首先,讓我們克隆 Spring Framework MVC 示例。 稍後,我們將修改 pom.xml 並用一個易受攻擊的版本替換現有的 Spring Framework 版本。 

克隆存儲庫:

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

在克隆的目錄中,編輯 pom.xml 以包含 5.0.0.RELEASE 作為 Spring Framework 版本:

<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/windows/system.ini'

%252e%252e%255c 是對 ..\ 的雙編碼,%255c%255c 是對 \\. 的雙編碼。

令人可悲的是,響應將是 Windows 系統文件 system.ini 的內容。

4. Spring Security HttpFirewall 接口

Servlet 規範 Servlet 規範 並不能精確地定義 servletPathpathInfo 之間的區別。因此,Servlet 容器在翻譯這些值時存在不一致性。

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

另一方面,以下代碼返回 null

request.getServletPath()

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

Spring Security 登場了。Spring Security 在容器中保持一致的行為,並規範化這些惡意 URL,利用 HttpFirewall 接口。該接口有 2 個實現:

4.1. DefaultHttpFirewall

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

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

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

但是,StrictHttpFirewall 提供了一個健壯且安全的實現,並且是推薦的實現。

4.2. StrictHttpFirewall

HttpFirewall.">StrictHttpFirewall 是 HttpFirewall 的默認且更嚴格的實現。 另一方面,與 DefaultHttpFirewall 不同,StrictHttpFirewall 會拒絕任何未規範化的 URL,從而提供更嚴格的保護。 此外,該實現可以保護應用程序免受跨站點跟蹤 (XST) 和 HTTP 謂詞篡改等其他攻擊:跨站點跟蹤 (XST)HTTP 謂詞篡改

此外,該實現是可定製的,並且具有合理的默認值。 換句話説,我們可以禁用(不推薦)一些功能,例如允許在 URI 中作為一部分使用分號:

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

簡而言之,StrictHttpFirewall 會拒絕可疑請求,並返回 org.springframework.security.web.firewall.RequestRejectedException

StrictHttpFirewall in action.">最後,讓我們使用 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 Authentication 來安全地保護我們的應用程序,通過創建一個配置類來創建 SecurityFilterChain 注入:

@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("用户創建成功");
    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,
      "用户已成功刪除", System.currentTimeMillis()));
}

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

mvn spring-boot:run

8. Testing the APIs

Now, let’s start by creating a User using cURL:

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

Here is a 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
}

Now, let’s configure our StrictHttpFirewallto deny requests from all the HTTP methods:

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

Next, let’s invoke the API again. Since we configured StrictHttpFirewall to restrict all the HTTP methods, this time, we get an error.

In the logs, we have this exception:

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 []

Since Spring Security v6.1.5, we can use RequestRejectedHandler to customize the HTTP Status when there is a RequestRejectedException:

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

Note that the default HTTP status code when using a HttpStatusRequestRejectedHandler is 400. However, we can customize this by passing a status code in the constructor of the HttpStatusRequestRejectedHandler class.

Now, let’s reconfigure the StrictHttpFirewall to allow \\ in the URL and HTTP GET, POST, DELETE, and OPTIONS methods:

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

Next, invoke the API:

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

And here we have a response:

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

Finally, let’s revert to the original strict functionality of StrictHttpFirewall by deleting the @Bean declaration.

Next, let’s try to invoke our API with suspicious URLs:

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

Straightaway, all the above requests fail with error log:

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 []

9. 結論

本文介紹了 Spring Security 防禦惡意 URL 的方法,這些 URL 可能會導致 Path Traversal/Directory Traversal 攻擊。

DefaultHttpFirewall 嘗試規範化惡意 URL。但是,StrictHttpFirewall 通過拋出 RequestRejectedException 來拒絕帶有惡意 URL 的請求。 此外,StrictHttpFirewall 還可以保護我們免受其他攻擊。 因此,強烈建議同時使用 StrictHttpFirewall 及其默認配置。

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

發佈 評論

Some HTML is okay.