1. 序章

Spring Frameworkバージョン5.0から5.0.4、4.3から4.3.14、およびその他の古いバージョンには、Windowsシステムにディレクトリまたはパストラバーサルのセキュリティの脆弱性がありました。

静的リソースの設定を誤ると、悪意のあるユーザーがサーバーのファイルシステムにアクセスできます。たとえば、file:protocolを使用して静的リソースを提供すると、Windowsのファイルシステムに不正にアクセスできます。

Spring Frameworkは脆弱性を認識し、それ以降のリリースで修正しました。

したがって、この修正により、パストラバーサル攻撃からアプリケーションが保護されます。 ただし、この修正により、以前のURLのいくつかはorg.springframework.security.web.firewall.RequestRejectedException例外をスローするようになりました。

最後に、このチュートリアルでは、パストラバーサル攻撃のコンテキストでorg.springframework.security.web.firewall.RequestRejectedExceptionとStrictHttpFirewallについて学習しましょう。

2. パストラバーサルの脆弱性

パストラバーサルまたはディレクトリトラバーサルの脆弱性により、Webドキュメントのルートディレクトリ外への不正アクセスが可能になります。 たとえば、URLを操作すると、ドキュメントルート外のファイルへの不正アクセスが発生する可能性があります。

最新の人気のあるWebサーバーはこれらの攻撃のほとんどを相殺しますが、攻撃者は「./」、「../」などの特殊文字のURLエンコードを使用して、Webサーバーのセキュリティを回避し、不正アクセスを取得できます。

また、 OWASP では、パストラバーサルの脆弱性とそれらに対処する方法について説明しています。

3. SpringFrameworkの脆弱性

それでは、修正方法を学ぶ前に、この脆弱性を再現してみましょう。

まず、SpringFrameworkMVCの例のクローンを作成しましょう。 後で、pom.xmlを変更して、既存のSpringFrameworkバージョンを脆弱なバージョンに置き換えましょう。

リポジトリのクローンを作成します。

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

クローン化されたディレクトリ内で、 pom.xml を編集して、SpringFrameworkバージョンとして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'

%252e %252e %255cは..\の二重エンコード形式であり、 %255c%255c\\の二重エンコード形式です。 。

不安定なことに、応答はWindowsシステムファイルsystem.ini。の内容になります

4. SpringセキュリティHttpFirewallインターフェース

The サーブレット仕様間の区別を正確に定義していません servletPath pathInfo。 したがって、これらの値の変換では、サーブレットコンテナ間に不整合があります。

たとえば、 Tomcat 9 では、URL http:// localhost:8080 / api / v1 / users / 1 の場合、URI /1が意図されていますパス変数になります。

一方、以下は / api / v1 / users /1を返します。

request.getServletPath()

ただし、以下のコマンドはnullを返します。

request.getPathInfo()

パス変数をURIから区別できないと、パストラバーサル/ディレクトリトラバーサル攻撃などの潜在的な攻撃につながる可能性があります。 たとえば、ユーザーはURLに \\、 /../、..\を含めることで、サーバー上のシステムファイルを悪用できます。 残念ながら、これらのURLを正規化するサーブレットコンテナは一部のみです。

春のセキュリティが救いの手を差し伸べます。 Spring Securityはコンテナ全体で一貫して動作し、HttpFirewallインターフェイスを利用してこれらの種類の悪意のあるURLを正規化します。 このインターフェースには2つの実装があります。

4.1. DefaultHttpFirewall

そもそも、実装クラスの名前と混同しないようにしましょう。 つまり、これはデフォルトのHttpFirewall実装ではありません。

ファイアウォールはURLをサニタイズまたは正規化しようとし、コンテナ全体でservletPathpathInfoを標準化します。 また、 @Bean を明示的に宣言することで、デフォルトのHttpFirewallの動作をオーバーライドできます。

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

ただし、 StrictHttpFirewall は、堅牢で安全な実装を提供し、推奨される実装です。

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を使用して疑わしいリクエストを拒否します。

最後に、 SpringRESTおよびSpringセキュリティを使用してユーザーでCRUD操作を行うユーザー管理アプリケーションを開発しましょう。StrictHttpFirewallを参照してください。 ]動作中。

5. 依存関係

SpringセキュリティSpringWebの依存関係を宣言しましょう。

<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セキュリティ構成

次に、 WebSecurityConfigurerAdapter を拡張する構成クラスを作成して、基本認証を使用してアプリケーションを保護しましょう。

@Configuration
public class SpringSecurityHttpFirewallConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
          .csrf()
          .disable()
          .authorizeRequests()
            .antMatchers("/error").permitAll()
          .anyRequest()
          .authenticated()
          .and()
          .httpBasic();
    }
}

デフォルトでは、SpringSecurityは再起動するたびに変更されるデフォルトのパスワードを提供します。 したがって、application.propertiesにデフォルトのユーザー名とパスワードを作成しましょう。

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

今後は、これらの資格情報を使用して、セキュリティで保護されたRESTAPIにアクセスします。

7. 安全なRESTAPIの構築

それでは、ユーザー管理RESTAPIを作成しましょう。

@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を使用してユーザーを作成することから始めましょう。

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]"
}

したがって、応答は次のようになります。

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をもう一度呼び出しましょう。 すべてのHTTPメソッドを制限するようにStrictHttpFirewallを構成したため、今回はエラーが発生します。

ログには、次の例外があります。

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 v5.4 以降、 RequestRejectedException がある場合、RequestRejectedHandlerを使用してHTTPステータスをカスタマイズできます。

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

を使用する場合のデフォルトのHTTPステータスコードに注意してください HttpStatusRequestRejectedHandler 400。 ただし、のコンストラクターでステータスコードを渡すことで、これをカスタマイズできます。 HttpStatusRequestRejectedHandler クラス。

次に、 StrictHttpFirewall を再構成して、URLに \\ を許可し、 HTTP GET POST DELETE 、および OPTIONS メソッド:

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<strong>\\</strong>v1/users

そして、ここに応答があります:

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

最後に、 @ Bean 宣言を削除して、StrictHttpFirewallの元の厳密な機能に戻しましょう。

次に、疑わしい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>//</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>\\</strong>users

すぐに、上記のすべての要求はエラーログで失敗します。

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

9. 結論

この記事では、パストラバーサル/ディレクトリトラバーサル攻撃を引き起こす可能性のある悪意のあるURLに対するSpringセキュリティの保護について説明します。

DefaultHttpFirewall は、悪意のあるURLを正規化しようとします。 ただし、 StrictHttpFirewall は、RequestRejectedExceptionでリクエストを拒否します。 パストラバーサル攻撃に加えて、StrictHttpFirewallは他のいくつかの攻撃から私たちを保護します。 したがって、デフォルト構成とともにStrictHttpFirewallを使用することを強くお勧めします。

いつものように、完全なソースコードはGithub利用できます。