SpringSecurity–リクエストが拒否された例外
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 と
たとえば、 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をサニタイズまたは正規化しようとし、コンテナ全体でservletPathとpathInfoを標準化します。 また、 @Bean を明示的に宣言することで、デフォルトのHttpFirewallの動作をオーバーライドできます。
@Bean
public HttpFirewall getHttpFirewall() {
return new DefaultHttpFirewall();
}
ただし、 StrictHttpFirewall は、堅牢で安全な実装を提供し、推奨される実装です。
4.2. StrictHttpFirewall
さらに、この実装はカスタマイズ可能であり、適切なデフォルトがあります。 つまり、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 は
次に、 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で利用できます。