Spring Security リファレンス ">

22. エクスプロイトに対する保護

22.1 WebFlux 環境向けのクロスサイトリクエストフォージェリ(CSRF)

このセクションでは、WebFlux 環境に対する Spring Security のクロスサイトリクエストフォージェリ (CSRF) サポートについて説明します。

22.1.1 Spring Security CSRF 保護の使用

Spring Security の CSRF 保護を使用する手順の概要は次のとおりです。

適切な HTTP 動詞を使用する

CSRF 攻撃から保護するための最初のステップは、Web サイトが適切な HTTP 動詞を使用するようにすることです。これについては、安全なメソッドはべき等でなければなりませんで詳しく説明しています。

CSRF 保護を構成する

次のステップは、Spring Security の CSRF 保護をアプリケーション内で構成することです。Spring Security の CSRF 保護はデフォルトで有効になっていますが、構成をカスタマイズする必要がある場合があります。以下は、いくつかの一般的なカスタマイズです。

カスタム CsrfTokenRepository

デフォルトでは、Spring Security は、WebSessionServerCsrfTokenRepository を使用して、期待される CSRF トークンを WebSession に保存します。ユーザーがカスタム ServerCsrfTokenRepository を構成したい場合があります。例: JavaScript ベースのアプリケーションサポートするために、CsrfToken  を Cookie に保持することが望ましい場合があり ます 。

デフォルトでは、CookieServerCsrfTokenRepository は XSRF-TOKEN という名前の Cookie に書き込み、X-XSRF-TOKEN という名前のヘッダーまたは HTTP パラメーター _csrf からそれを読み取ります。これらのデフォルトは AngularJS (英語) からのものです

以下を使用して、Java 構成で CookieCsrfTokenRepository を構成できます。

例 22.1: Java 構成で Cookie に CSRF トークンを保存する

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .csrf(csrf -> csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()))
    return http.build();
}

[Note] メモ

サンプルは cookieHttpOnly=false を明示的に設定します。これは、JavaScript(つまり、AngularJS)が読み取れるようにするために必要です。JavaScript で Cookie を直接読み取る機能が必要ない場合は、cookieHttpOnly=false を省略して(代わりに new CookieServerCsrfTokenRepository() を使用して)セキュリティを向上させることをお勧めします。

CSRF 保護を無効にする

CSRF 保護はデフォルトで有効になっています。ただし、アプリケーションにとって意味がある場合、CSRF 保護を無効にするのは簡単 です 。

以下の Java 構成は、CSRF 保護を無効にします。

例 22.2: CSRF Java 構成を無効にする

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .csrf(csrf -> csrf.disable()))
    return http.build();
}

CSRF トークンを含める

シンクロナイザートークンパターンを CSRF 攻撃から保護するには、実際の CSRF トークンを HTTP リクエストに含める必要があります。これは、ブラウザーによって HTTP リクエストに自動的に含まれないリクエストの一部(フォームパラメーター、HTTP ヘッダーなど)に含まれる必要があります。

Spring Security の CsrfWebFilter (Javadoc) は、Mono <CsrfToken> (Javadoc) を org.springframework.security.web.server.csrf.CsrfToken という名前の ServerWebExchange 属性として公開します。これは、任意のビューテクノロジが Mono<CsrfToken> にアクセスして、期待されるトークンをフォームまたはメタタグとして公開できることを意味します。

ビューテクノロジーが Mono<CsrfToken> をサブスクライブする簡単な方法を提供しない場合、一般的なパターンは Spring の @ControllerAdvice を使用して CsrfToken を直接公開することです。例: 次のコードは、Spring Security の CsrfRequestDataValueProcessor によって使用されるデフォルトの属性名(_csrf)に CsrfToken を配置して、CSRF トークンを非表示の入力として自動的に含めます。

例 22.3: @ModelAttribute としての CsrfToken 

@ControllerAdvice
public class SecurityControllerAdvice {
    @ModelAttribute
    Mono<CsrfToken> csrfToken(ServerWebExchange exchange) {
        Mono<CsrfToken> csrfToken = exchange.getAttribute(CsrfToken.class.getName());
        return csrfToken.doOnSuccess(token -> exchange.getAttributes()
                .put(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME, token));
    }
}

幸いなことに、Thymeleaf は追加作業なしで機能する統合を提供します。

エンコードされたフォーム URL

HTML フォームを送信するには、CSRF トークンを非表示の入力としてフォームに含める必要があります。例: レンダリングされた HTML は次のようになります。

例 22.4: CSRF トークン HTML

<input type="hidden"
    name="_csrf"
    value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>

次に、CSRF トークンを非表示入力としてフォームに含めるさまざまな方法について説明します。

自動 CSRF トークンの包含

Spring Security の CSRF サポートは、CsrfRequestDataValueProcessor (Javadoc) を介して Spring の RequestDataValueProcessor (Javadoc) との統合を提供します。CsrfRequestDataValueProcessor が機能するためには、Mono<CsrfToken> をサブスクライブし、CsrfToken を DEFAULT_CSRF_ATTR_NAME (Javadoc) に一致 する属性として公開する必要があります。

幸いなことに、Thymleaf (英語) は 、RequestDataValueProcessor と統合して、安全でない HTTP メソッド(つまり、ポスト)を持つフォームに実際の CSRF トークンが自動的に含まれるようにすることで、すべての定型文を処理 するサポート (英語) 提供し (英語) ます。

CsrfToken リクエスト属性

リクエストに実際の CSRF トークンを含めるための他のオプションが機能しない場合、Mono<CsrfToken> が org.springframework.security.web.server.csrf.CsrfToken という名前の ServerWebExchange 属性として公開さ れているという事実を利用できます。

以下の Thymeleaf サンプルは、_csrf という名前の属性で CsrfToken を公開することを想定しています。

例 22.5: リクエスト属性を持つフォームの CSRF トークン

<form th:action="@{/logout}"
    method="post">
<input type="submit"
    value="Log out" />
<input type="hidden"
    th:name="${_csrf.parameterName}"
    th:value="${_csrf.token}"/>
</form>

Ajax および JSON リクエスト

JSON を使用している場合、HTTP パラメーター内で CSRF トークンを送信することはできません。代わりに、HTTP ヘッダー内でトークンを送信できます。

次のセクションでは、JavaScript ベースのアプリケーションで CSRF トークンを HTTP リクエストヘッダーとして含めるさまざまな方法について説明します。

自動包含

Spring Security は、予想される CSRF トークンを Cookie に保存する よう に簡単に構成できます。期待される CSRF を Cookie に保存することにより、AngularJS (英語) などの JavaScript フレームワークは、実際の CSRF トークンを HTTP リクエストヘッダーに自動的に含めます。

メタタグ

Cookie で CSRF公開する別のパターンは、meta タグ内に CSRF トークンを含めることです。HTML は次のようになります。

例 22.6: CSRF メタタグ HTML

<html>
<head>
    <meta name="_csrf" content="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
    <meta name="_csrf_header" content="X-CSRF-TOKEN"/>
    <!-- ... -->
</head>
<!-- ... -->

メタタグに CSRF トークンが含まれると、JavaScript コードはメタタグを読み取り、CSRF トークンをヘッダーとして含めます。jQuery を使用している場合、これは次の方法で実行できます。

例 22.7: AJAX 送信 CSRF トークン

$(function () {
    var token = $("meta[name='_csrf']").attr("content");
    var header = $("meta[name='_csrf_header']").attr("content");
    $(document).ajaxSend(function(e, xhr, options) {
        xhr.setRequestHeader(header, token);
    });
});

以下のサンプルでは、_csrf という名前の属性で CsrfToken を公開することを想定しています。Thymeleaf を使用してこれを実行する例を以下に示します。

例 22.8: CSRF メタタグ JSP

<html>
<head>
    <meta name="_csrf" th:content="${_csrf.token}"/>
    <!-- default header name is X-CSRF-TOKEN -->
    <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
    <!-- ... -->
</head>
<!-- ... -->

22.1.2 CSRF の考慮事項

CSRF 攻撃に対する保護を実装する際に考慮すべきいくつかの特別な考慮事項があります。このセクションでは、WebFlux 環境に関連する考慮事項について説明します。より一般的な説明については、「CSRF に関する考慮事項」と呼ばれるセクションを 参照し て ください。

ログイン

ログインリクエストの偽造を防ぐために 、ログインリクエストに CSRFリクエストすることが重要 です 。Spring Security の WebFlux サポートは、これをすぐに実行できます。

ログアウト

ログアウト試行の偽造を防ぐために 、ログアウトリクエストに CSRFリクエストすることが重要 です 。デフォルトでは、Spring Security の LogoutWebFilter は HTTP ポストリクエストのみを処理します。これにより、ログアウトに CSRF トークンが必要になり、悪意のあるユーザーがユーザーを強制的にログアウトできなくなります。

最も簡単な方法は、フォームを使用してログアウトすることです。本当にリンクが必要な場合は、JavaScript を使用して、リンクに POST を実行させることができます(つまり、非表示のフォームで)。JavaScript が無効になっているブラウザーの場合、オプションで、POST を実行するログアウト確認ページにユーザーをリンクさせることができます。

本当にログアウトで HTTP GET を使用したい場合は使用できますが、これは一般的に推奨されないことを忘れないでください。例: 次の Java 構成は、任意の HTTP メソッドで /logout がリクエストされた URL でログアウトを実行します。

例 22.9: HTTP GET でログアウトする

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .logout(logout -> logout.requiresLogout(new PathPatternParserServerWebExchangeMatcher("/logout")))
    return http.build();
}

CSRF およびセッションタイムアウト

デフォルトでは、Spring Security は CSRF トークンを WebSession に保存します。これにより、セッションの有効期限が切れる状況が発生する可能性があります。つまり、検証対象の CSRF トークンが存在しないことがあります。

セッションタイムアウトの一般的なソリューションについてはすでに説明しました。このセクションでは、WebFlux サポートに関連する CSRF タイムアウトの詳細について説明します。

予想される CSRF トークンのストレージを Cookie に変更するのは簡単です。詳細については、「カスタム CsrfTokenRepository」セクション と呼ばれるセクションを 参照してください。

マルチパート (ファイルアップロード)

CSRF 攻撃からマルチパートリクエスト(ファイルアップロード)を保護することにより、鶏と卵 (英語) の 問題がどのように発生するか について はすでに説明しました。このセクションでは、WebFlux アプリケーション内の本文URL に CSRF トークンを配置する方法を説明します。

[Note] メモ

Spring でのマルチパートフォームの使用に関する詳細情報は、Spring リファレンスのマルチパートデータセクションにあります。

CSRF トークンを本文に配置する

CSRF トークンを本文に配置することのトレードオフ について はすでに説明しました。

WebFlux アプリケーションでは、これは次の構成で構成できます。

例 22.10: multipart/form-data から CSRF トークンの取得を有効にする

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .csrf(csrf -> csrf.tokenFromMultipartDataEnabled(true))
    return http.build();
}

URL に CSRF トークンを含める

CSRF トークンを URL に配置することのトレードオフ について はすでに説明しました。CsrfToken は ServerHttpRequest  リクエスト属性として公開されているため、それを使用して CSRF トークンを含む action を作成できます。Thymeleaf の例を以下に示します。

例 22.11: 実行中の CSRF トークン

<form method="post"
    th:action="@{/upload(${_csrf.parameterName}=${_csrf.token})}"
    enctype="multipart/form-data">

HiddenHttpMethodFilter

HTTP メソッドのオーバーライド について はすでに説明しました

Spring WebFlux アプリケーションでは、HiddenHttpMethodFilter (Javadoc) を使用して HTTP メソッドをオーバーライドします。

22.2 セキュリティ HTTP レスポンスヘッダー

セキュリティ HTTP レスポンスヘッダーを使用して、Web アプリケーションのセキュリティを強化できます。このセクションは、セキュリティ HTTP レスポンスヘッダーの WebFlux ベースのサポート専用です。

22.2.1 デフォルトのセキュリティヘッダー

Spring Security は、セキュリティ HTTP レスポンスヘッダーのデフォルトセットを提供して、安全なデフォルトを提供します。これらのヘッダーはそれぞれベストプラクティスと見なされますが、すべてのクライアントがヘッダーを使用するわけではないため、追加のテストが推奨されます。

特定のヘッダーをカスタマイズできます。例: X-Frame-Options に SAMEORIGIN を指定する場合を除き、デフォルトが必要であると想定します。

これは、次の Java 構成を使用して簡単に実行できます。

例 22.12: Java 構成でデフォルトのセキュリティヘッダーをカスタマイズする

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .headers(headers ->
            headers
                .frameOptions(frameOptions ->
                    frameOptions
                        .mode(Mode.SAMEORIGIN)
                )
        );
    return http.build();
}

デフォルトの追加を望まず、使用するものを明示的に制御したい場合は、デフォルトを無効にできます。両方の Java 構成の例を以下に示します。

例 22.13: HTTP セキュリティレスポンスヘッダーを無効にする

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .headers(headers ->
            headers
                .disable()
        );
    return http.build();
}

22.2.2 キャッシュ制御

Spring Security には、デフォルトでキャッシュ制御ヘッダーが含まれています。

ただし、実際に特定のレスポンスをキャッシュしたい場合、アプリケーションはそれらを ServerHttpResponse (Javadoc) に選択的に追加して、Spring Security によって設定されたヘッダーをオーバーライドできます。これは、CSS、JavaScript、イメージなどを適切にキャッシュできます。

Spring WebFluxZz を使用する場合、これは通常、構成内で行われます。これを行う方法の詳細については、Spring リファレンスドキュメントの静的リソースの部分を参照してください。

必要に応じて、Spring Security のキャッシュ制御 HTTP レスポンスヘッダーを無効にすることもできます。

例 22.14: キャッシュ制御が無効

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .headers(headers ->
            headers
                .cache(cache -> cache.disable())
        );
    return http.build();
}

22.2.3 コンテンツタイプオプション

Spring Security には、デフォルトでコンテンツタイプヘッダーが含まれています。ただし、次の方法で Java 構成で無効にできます。

例 22.15: Java 構成で無効にされたコンテンツタイプオプション

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .headers(headers ->
            headers
                .contentTypeOptions(contentTypeOptions -> contentTypeOptions.disable())
        );
    return http.build();
}

22.2.4 HTTP Strict Transport Security (HSTS)

Spring Security は、デフォルトで Strict Transport Security ヘッダーを提供します。ただし、結果を明示的にカスタマイズできます。例: 以下は、HSTS に Java 構成を明示的に提供する例です。

例 22.16: Java 構成の Strict Transport Security

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .headers(headers ->
            headers
                .hsts(hsts ->
                    hsts
                        .includeSubdomains(true)
                        .preload(true)
                        .maxAge(Duration.ofDays(365))
                )
        );
    return http.build();
}

22.2.5 X-Frame-Options

デフォルトでは、Spring Security は X-Frame-Options を使用して iframe 内のレンダリングを無効にします。

以下を使用して、Java オプション内で同じオリジンを使用するようにフレームオプションをカスタマイズできます。

例 22.17: X-Frame-Options: SAMEORIGIN

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .headers(headers ->
            headers
                .frameOptions(frameOptions ->
                    frameOptions
                        .mode(SAMEORIGIN)
                )
        );
    return http.build();
}

22.2.6 X-XSS-Protection

デフォルトでは、Spring Security は、<< headers-xss-protection、X-XSS-Protection header> を使用して、リフレクションされた XSS 攻撃をブロックするようブラウザーに指示します。次の Java 構成で X-XSS-Protection を無効にできます。

例 22.18: X-XSS-Protection のカスタマイズ

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .headers(headers ->
            headers
                .xssProtection(xssProtection -> xssProtection.disable())
        );
    return http.build();
}

22.2.7 コンテンツセキュリティポリシー (CSP)

Spring Security はデフォルトではコンテンツセキュリティポリシーを追加しません。これは、アプリケーションのコンテキストなしでは妥当なデフォルトを知ることが不可能です。Web アプリケーションの作成者は、保護されたリソースを強制または監視するためのセキュリティポリシーを宣言する必要があります。

例: 次のセキュリティポリシーが与えられた場合:

例 22.19: コンテンツセキュリティポリシーの例

Content-Security-Policy: script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/

以下に示すように、Java 構成を使用して CSP ヘッダーを有効にできます。

例 22.20: コンテンツセキュリティポリシー

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .headers(headers ->
            headers
                .contentSecurityPolicy(contentSecurityPolicy ->
                    contentSecurityPolicy
                        .policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
                )
        );
    return http.build();
}

CSP report-only ヘッダーを有効にするには、次の Java 構成を提供します。

例 22.21: コンテンツセキュリティポリシーレポートのみ

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .headers(headers ->
            headers
                .contentSecurityPolicy(contentSecurityPolicy ->
                    contentSecurityPolicy
                        .policyDirectives("script-src 'self' https://trustedscripts.example.com; object-src https://trustedplugins.example.com; report-uri /csp-report-endpoint/")
                        .reportOnly()
                )
        );
    return http.build();
}

22.2.8 リファラーポリシー

Spring Security は、デフォルトではリファラーポリシーヘッダーを追加しません。以下に示すように、Java 構成を使用してリファラーポリシーヘッダーを有効にできます。

例 22.22: リファラーポリシー Java 構成

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .headers(headers ->
            headers
                .referrerPolicy(referrerPolicy ->
                    referrerPolicy
                        .policy(ReferrerPolicy.SAME_ORIGIN)
                )
        );
    return http.build();
}

22.2.9 機能ポリシー

Spring Security は、デフォルトでは機能ポリシーヘッダーを追加しません。次の Feature-Policy ヘッダー:

例 22.23: 機能ポリシーの例

Feature-Policy: geolocation 'self'

以下に示すように、Java 構成を使用して機能ポリシーヘッダーを有効にできます。

例 22.24: 機能ポリシー Java 構成

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .headers(headers ->
            headers
                .featurePolicy("geolocation 'self'")
        );
    return http.build();
}

22.2.10 サイトデータを消去する

Spring Security は、デフォルトではクリアサイトデータヘッダーを追加しません。次の Clear-Site-Data ヘッダー:

例 22.25: クリアサイトデータの例

Clear-Site-Data: "cache", "cookies"

ログアウト時に次の構成で送信できます。

例 22.26: Clear-Site-Data Java 構成

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    ServerLogoutHandler securityContext = new SecurityContextServerLogoutHandler();
    ClearSiteDataServerHttpHeadersWriter writer = new ClearSiteDataServerHttpHeadersWriter(CACHE, COOKIES);
    ServerLogoutHandler clearSiteData = new HeaderWriterServerLogoutHandler(writer);
    DelegatingServerLogoutHandler logoutHandler = new DelegatingServerLogoutHandler(securityContext, clearSiteData);

    http
        // ...
        .logout()
            .logoutHandler(logoutHandler);
    return http.build();
}

22.3 HTTP

すべての HTTP ベースの通信は、 TLS を使用して保護 する 必要があり ます 。

以下に、HTTPS の使用を支援する WebFlux 固有の機能に関する詳細を示します。

22.3.1 HTTPS にリダイレクト

クライアントが HTTPS ではなく HTTP を使用してリクエストを行う場合、Spring Security は HTTPS にリダイレクトするように構成できます。

例: 次の Java 構成は、HTTP リクエストを HTTPS にリダイレクトします。

例 22.27: HTTPS にリダイレクト

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .redirectToHttps(withDefaults());
    return http.build();
}

構成は、本番環境でのみ有効にする if ステートメントに簡単にラップできます。または、本番環境でのみ発生するリクエストに関するプロパティを検索することで有効にできます。例: 本番環境が X-Forwarded-Proto という名前のヘッダーを追加する場合、次の Java 構成を使用できます。

例 22.28: X-Forwarded 時に HTTPS にリダイレクトする

@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    http
        // ...
        .redirectToHttps(redirectToHttps ->
            redirectToHttps
                .httpsRedirectWhen(e -> e.getRequest().getHeaders().containsKey("X-Forwarded-Proto"))
        );
    return http.build();
}

22.3.2 Strict Transport Security

Spring Security は Strict Transport Security をサポートし、デフォルトで有効にします。

22.3.3 プロキシサーバー構成

Spring Security はプロキシサーバーと統合します

現行バージョンへ切り替える