Spring Security – Request Rejected Exception – Spring Security – Request Rejected Exception

最后修改: 2021年 10月 1日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

Spring Framework versions 5.0 to 5.0.4, 4.3 to 4.3.14, and other older versions had a directory or path traversal security vulnerability on Windows systems.

Spring Framework 5.0至5.0.4、4.3至4.3.14等旧版本在Windows系统上存在目录或路径穿越的安全漏洞。

Misconfiguring the static resources allows malicious users to access the server’s file system. For instance, serving static resources using file: protocol provides illegal access to the file system on Windows.

对静态资源的错误配置允许恶意用户访问服务器的文件系统。例如,使用file:协议提供静态资源,在Windows上提供对文件系统的非法访问

The Spring Framework acknowledged the vulnerability and fixed it in the later releases.

Spring框架承认该漏洞,并在后来的版本中修复了该漏洞。

Consequently, this fix guards the applications against path traversal attacks. However, with this fix, a few of the earlier URLs now throw an org.springframework.security.web.firewall.RequestRejectedException exception.

因此,这项修复可以防止应用程序受到路径遍历攻击。然而,通过这一修复,一些早期的URL现在会抛出一个org.springframework.security.web.firewall.RequestRejectedException 异常

Finally, in this tutorial, let’s learn about org.springframework.security.web.firewall.RequestRejectedException and StrictHttpFirewall in the context of path traversal attacks.

最后,在本教程中,我们来了解一下org.springframework.security.web.firewall.RequestRejectedExceptionStrictHttpFirewall在路径遍历攻击方面的情况

2. Path Traversal Vulnerabilities

2.路径遍历漏洞

A path traversal or directory traversal vulnerability enables illegal access outside the web document root directory. For instance, manipulating the URL can provide unauthorized access to the files outside the document root.

路径遍历或目录遍历漏洞可以在网络文档根目录之外进行非法访问。例如,操纵URL可以提供对文档根目录以外的文件的非法访问。

Though most latest and popular webservers offset most of these attacks, the attackers can still use URL-encoding of special characters like “./”, “../” to circumvent the webserver security and gain illegal access.

尽管大多数最新和流行的网络服务器抵消了大部分的这些攻击,但攻击者仍然可以使用”./”、”./”等特殊字符的URL编码来规避网络服务器的安全并获得非法访问。

Also, OWASP discusses the Path Traversal vulnerabilities and the ways to address them.

此外,OWASP还讨论了路径遍历漏洞以及解决这些漏洞的方法。

3. Spring Framework Vulnerability

3.Spring框架的漏洞

Now, Let’s try to replicate this vulnerability before we learn how to fix it.

现在,让我们在学习如何修复这个漏洞之前尝试复制这个漏洞。

First, let’s clone the Spring Framework MVC examples. Later, let’s modify the pom.xml and replace the existing Spring Framework version with a vulnerable version. 

首先,让我们克隆Spring Framework MVC的例子。随后,让我们修改pom.xml ,用一个脆弱的版本替换现有的Spring Framework版本。

Clone the repository:

克隆版本库。

git clone git@github.com:spring-projects/spring-mvc-showcase.git

Inside the cloned directory, edit the pom.xml to include 5.0.0.RELEASE as the Spring Framework version:

在克隆的目录中,编辑pom.xml以包括5.0.0.RELEASE作为Spring框架的版本。

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

Next, edit the web configuration class WebMvcConfig and modify the addResourceHandlers method to map resources to a local file directory using  file:

接下来,编辑网络配置类WebMvcConfig ,并修改addResourceHandlers 方法,使用file:将资源映射到本地文件目录。

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

Later, build the artifact and run our web app:

之后,构建工件并运行我们的网络应用。

mvn jetty:run

Now, when the server starts up, invoke the URL:

现在,当服务器启动时,调用该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'

%252e%252e%255c is a double-encoded form of  ..\ and %255c%255c is a double-encoded form of \\.

%252e%252e%255c是.的双重编码形式,%255c%255c\的双重编码形式。

Precariously, the response will be the contents of the Windows system file system.ini.

通常情况下,响应将是Windows系统文件system.ini.的内容。

4. Spring Security HttpFirewall Interface

4.Spring Security HttpFirewall 接口

The Servlet specification does not precisely define the distinction between servletPath and pathInfo. Hence, there is an inconsistency among the Servlet containers in the translation of these values.

Servlet 规范并没有精确定义servletPathpathInfo 之间的区别。因此,Servlet容器在翻译这些值时存在不一致的情况。

For instance, on Tomcat 9, for the URL http://localhost:8080/api/v1/users/1, the URI /1 is intended to be a path variable.

例如,在Tomcat 9上,对于URL http://localhost:8080/api/v1/users/1,URI /1 被打算成为一个路径变量。

On the other hand, the following returns /api/v1/users/1:

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

request.getServletPath()

However, the command below returns a null:

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

request.getPathInfo()

Unable to distinguish the path variables from the URI can lead to potential attacks like Path Traversal / Directory Traversal attacks. For instance, a user can exploit system files on the server by including a \\,  /../, ..\ in the URL. Unfortunately, only some Servlet containers normalize these URLs.

无法从URI中区分出路径变量会导致潜在的攻击,如路径遍历/目录遍历攻击。例如,用户可以通过在URL中包含\\, /../, ..\来利用服务器上的系统文件。不幸的是,只有一些Servlet容器将这些URL规范化。

Spring Security to the rescue. Spring Security consistently behaves across the containers and normalizes these kinds of malicious URLs utilizing a HttpFirewall interface. This interface has two implementations:

Spring Security来拯救我们。Spring Security在各容器中表现一致,并利用HttpFirewall 接口对这类恶意的URL进行规范。这个接口有两个实现。

4.1. DefaultHttpFirewall

4.1.DefaultHttpFirewall

In the first place, let’s not get confused with the name of the implementation class. In other words, this is not the default HttpFirewall implementation.

首先,我们不要被这个实现类的名字所迷惑。换句话说,这不是默认的HttpFirewall实现。

The firewall tries to sanitize or normalize the URLs and standardizes the servletPath and pathInfo across the containers. Also, we can override the default HttpFirewall behavior by explicitly declaring a @Bean:

防火墙试图对URL进行消毒或规范化处理,并在各容器中对servletPathpathInfo进行标准化。另外,我们可以通过明确地声明一个@Bean来覆盖默认的HttpFirewall行为。

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

However, StrictHttpFirewall provides a robust and secured implementation and is the recommended implementation.

然而,StrictHttpFirewall提供了一个强大和安全的实现,是推荐的实现。

4.2. StrictHttpFirewall

4.2.StrictHttpFirewall

StrictHttpFirewall is the default and stricter implementation of HttpFirewall. In contrast, unlike DefaultHttpFirewall, StrictHttpFirewall rejects any un-normalized URLs providing more stringent protection. In addition, this implementation protects the application from several other attacks like Cross-Site Tracing (XST) and HTTP Verb Tampering.

StrictHttpFirewallHttpFirewall的默认和更严格的实现。相比之下,与DefaultHttpFirewall不同,StrictHttpFirewall会拒绝任何未规范化的URL,提供更严格的保护。此外,该实现还可以保护应用程序免受其他一些攻击,如跨站追踪(XST)HTTP Verb Tampering

Moreover, this implementation is customizable and has sensible defaults. In other words, we can disable (not recommended) a few of the features like allowing semicolons as part of the URI:

此外,这个实现是可定制的,有合理的默认值。换句话说,我们可以禁用(不推荐)一些功能,比如允许分号作为URI的一部分。

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

In short, StrictHttpFirewall rejects suspicious requests with a org.springframework.security.web.firewall.RequestRejectedException.

简而言之,StrictHttpFirewallorg.springframework.security.web.firewall.RequestRejectedException拒绝可疑的请求。

Finally, let’s develop a User Management application with CRUD operations on Users using Spring REST and Spring Security, and see StrictHttpFirewall in action.

最后,让我们使用Spring RESTSpring Security开发一个对用户进行CRUD操作的用户管理应用程序,并看看StrictHttpFirewall的运作。

5. Dependencies

5.依赖性

Let’s declare Spring Security and Spring Web dependencies:

让我们声明Spring SecuritySpring Web依赖。

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

6. Spring Security Configuration

6.Spring安全配置

Next, let’s secure our application with Basic Authentication by creating a configuration class that creates a SecurityFilterChain bean:

接下来,让我们通过创建一个配置类来保护我们的应用程序的基本认证,该类创建了一个SecurityFilterChainbean。

@Configuration
public class HttpFirewallConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf()
            .disable()
            .authorizeRequests()
            .antMatchers("/error")
            .permitAll()
            .anyRequest()
            .authenticated()
            .and()
            .httpBasic();
        return http.build();
    }
}

By default, Spring Security provides a default password that changes for every restart. Hence, let’s create a default username and password in the application.properties:

默认情况下,Spring Security提供了一个默认密码,每次重启都会改变。因此,让我们在application.properties中创建一个默认的用户名和密码。

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

Henceforth, we’ll access our secured REST APIs using these credentials.

此后,我们将使用这些证书访问我们的安全REST APIs。

7. Building a Secured REST API

7.构建一个安全的REST API

Now, let’s build our User Management 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()));
}

Now, let’s build and run the application:

现在,让我们构建并运行该应用程序。

mvn spring-boot:run

8. Testing the APIs

8.测试API

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

现在,让我们开始使用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

Here is a request.json:

这里有一个request.json

{
    "id":"1",
    "username":"navuluri",
    "email":"bhaskara.navuluri@mail.com"
}

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 StrictHttpFirewall to deny requests from all the HTTP methods:

现在,让我们配置我们的StrictHttpFirewall,拒绝来自所有HTTP方法的请求。

@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.

接下来,让我们再次调用API。由于我们配置了StrictHttpFirewall来限制所有的HTTP方法,这一次,我们得到了一个错误。

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 v5.4, we can use RequestRejectedHandler to customize the HTTP Status when there is a RequestRejectedException:

Spring Security v5.4以来,我们可以使用RequestRejectedHandler来定制HTTP状态,当出现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.

请注意,当使用HttpStatusRequestRejectedHandler时,默认的HTTP状态代码是400。然而,我们可以通过在HttpStatusRequestRejectedHandler类的构造函数中传递一个状态代码来定制这个。

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

现在,让我们重新配置StrictHttpFirewall,允许\在URL和HTTP GETPOSTDELETEOPTIONS方法。

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

Next, invoke the API:

接下来,调用API。

curl -i --user user:password -d @request.json -H "Content-Type: application/json" 
     -H "Accept: application/json" http://localhost:8080/api<strong>\\>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.

最后,让我们通过删除@Bean声明,恢复到StrictHttpFirewall的原始严格功能。

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

接下来,让我们尝试用可疑的URL来调用我们的API。

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

Straightaway, all the above requests fail with error log:

直接,所有上述请求都失败了,并有错误记录。

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

9. Conclusion

9.结语

This article explains Spring Security’s protection against malicious URLs that may cause the Path Traversal/Directory Traversal attacks.

本文解释了Spring Security对可能导致路径遍历/目录遍历攻击的恶意URL的保护。

DefaultHttpFirewall tries to normalize the malicious URLs. However, StrictHttpFirewall rejects the requests with a RequestRejectedException. Along with Path Traversal attacks, StrictHttpFirewall protects us from several other attacks. Hence it is highly recommended to use the StrictHttpFirewall along with its default configurations.

DefaultHttpFirewall试图将恶意的URL正常化。然而,StrictHttpFirewallRequestRejectedException拒绝了这些请求。除了路径遍历攻击外,StrictHttpFirewall还可以保护我们免受其他一些攻击。因此,强烈建议使用StrictHttpFirewall以及其默认配置。

As always, the complete source code is available over on Github.

一如既往,完整的源代码可在Github上获得。