最新の安定バージョンについては、Spring Security 6.3.1 を使用してください!

サーブレット環境のクロスサイトリクエストフォージェリ(CSRF)

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

Spring Security CSRF 保護の使用

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

適切な HTTP 動詞を使用する

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

CSRF 保護を構成する

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

カスタム CsrfTokenRepository

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

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

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

XML 設定で Cookie に CSRF トークンを保存する
<http>
	<!-- ... -->
	<csrf token-repository-ref="tokenRepository"/>
</http>
<b:bean id="tokenRepository"
	class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
	p:cookieHttpOnly="false"/>

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

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

CSRF トークンを Cookie に保存する
  • Java

  • Kotlin

@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.csrf(csrf -> csrf
				.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
			);
		return http.build();
	}
}
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
       http {
            csrf {
                csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse()
            }
        }
        return http.build()
    }
}

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

CSRF 保護を無効にする

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

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

CSRF XML 設定を無効にする
<http>
	<!-- ... -->
	<csrf disabled="true"/>
</http>

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

CSRF を無効にする
  • Java

  • Kotlin

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.csrf(csrf -> csrf.disable());
		return http.build();
	}
}
@Configuration
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
       http {
            csrf {
                disable()
            }
        }
        return http.build()
    }
}

CSRF トークンを含める

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

Spring Security の CsrfFilter (Javadoc) は、CsrfToken (Javadoc) を _csrf という名前の HttpServletRequest 属性として公開します。これは、任意のビューテクノロジが CsrfToken にアクセスして、期待されるトークンをフォームまたはメタタグとして公開できることを意味します。幸いなことに、トークンをフォームおよび ajax リクエストにさらに簡単に含めることができる統合が以下にリストされています。

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

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

CSRF トークン HTML
<input type="hidden"
	name="_csrf"
	value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>

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

自動 CSRF トークンの包含

Spring Security の CSRF サポートは、CsrfRequestDataValueProcessor (Javadoc) を介して Spring の RequestDataValueProcessor (Javadoc) との統合を提供します。これは、Spring のフォームタグライブラリThymeleaf (英語) RequestDataValueProcessor と統合する他のビューテクノロジーを活用する場合、安全でない HTTP メソッド(つまり、投稿)を持つフォームには実際の CSRF トークンが自動的に含まれることを意味します。

csrfInput タグ

JSP を使用している場合は、Spring のフォームタグライブラリを使用できます。ただし、それがオプションでない場合は、csrfInput タグを使用してトークンを簡単に含めることもできます。

CsrfToken リクエスト属性

リクエストに実際の CSRF トークンを含めるための他のオプションが機能しない場合、CsrfToken が _csrf という名前の HttpServletRequest 属性として公開されているという事実を利用できます。

JSP でこれを実行する例を以下に示します。

リクエスト属性を持つフォームの CSRF トークン
<c:url var="logoutUrl" value="/logout"/>
<form action="${logoutUrl}"
	method="post">
<input type="submit"
	value="Log out" />
<input type="hidden"
	name="${_csrf.parameterName}"
	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 は次のようになります。

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 を使用している場合、これは次の方法で実行できます。

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);
	});
});
csrfMeta タグ

JSP を使用している場合、CSRF トークンを meta タグに書き込む簡単な方法は、csrfMeta タグを利用することです。

CsrfToken リクエスト属性

リクエストに実際の CSRF トークンを含めるための他のオプションが機能しない場合、CsrfToken が _csrf という名前の HttpServletRequest 属性として公開されているという事実を利用できます。JSP でこれを実行する例を以下に示します。

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

CSRF の考慮事項

CSRF 攻撃に対する保護を実装する際に考慮すべき特別な考慮事項がいくつかあります。このセクションでは、サーブレット環境に関連する考慮事項について説明します。より一般的な議論については、CSRF の考慮事項を参照してください。

ログイン

ログイン試行の偽造から保護するために、ログインリクエストに CSRF をリクエストすることが重要です。Spring Security のサーブレットサポートは、これをそのまま実行します。

ログアウト

ログアウトの試行を偽造しないように保護するために、ログアウトリクエストに CSRF をリクエストすることが重要です。CSRF 保護が有効になっている場合(デフォルト)、Spring Security の LogoutFilter は HTTP POST のみを処理します。これにより、ログアウトに CSRF トークンが必要になり、悪意のあるユーザーがユーザーを強制的にログアウトできないようになります。

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

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

HTTP GET でログアウトする
  • Java

  • Kotlin

@EnableWebSecurity
public class WebSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.logout(logout -> logout
				.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
			);
		return http.build();
	}
}
@EnableWebSecurity
class SecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
       http {
            logout {
                logoutRequestMatcher = AntPathRequestMatcher("/logout")
            }
        }
        return http.build()
    }
}

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

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

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

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

トークンの有効期限が切れた場合は、カスタム AccessDeniedHandler を指定して、トークンの処理方法をカスタマイズすることをお勧めします。カスタム AccessDeniedHandler は、InvalidCsrfTokenException を任意の方法で処理できます。AccessDeniedHandler をカスタマイズする方法の例については、xmlJava 構成 [GitHub] (英語) の両方に提供されているリンクを参照してください。

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

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

Spring でマルチパートフォームを使用する方法の詳細については、Spring リファレンスの 1.1.11. マルチパートリゾルバーセクションおよび MultipartFilter javadoc を参照してください。

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

CSRF トークンを本体に配置することのトレードオフについてはすでに説明しました。このセクションでは、本体から CSRF を読み取るように Spring Security を構成する方法について説明します。

本体から CSRF トークンを読み取るために、MultipartFilter は Spring Security フィルターの前に指定されます。Spring Security フィルターの前に MultipartFilter を指定すると、MultipartFilter を呼び出す権限がないため、だれでもサーバーに一時ファイルを配置できます。ただし、認可されたユーザーのみが、アプリケーションで処理されるファイルを送信できます。一般的に、一時ファイルのアップロードはほとんどのサーバーにほとんど影響を与えないはずなので、これが推奨されるアプローチです。

java 構成の Spring Security フィルターの前に MultipartFilter が指定されるようにするために、ユーザーは、次に示すように beforeSpringSecurityFilterChain をオーバーライドできます。

イニシャライザー MultipartFilter
  • Java

  • Kotlin

public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

	@Override
	protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
		insertFilters(servletContext, new MultipartFilter());
	}
}
class SecurityApplicationInitializer : AbstractSecurityWebApplicationInitializer() {
    override fun beforeSpringSecurityFilterChain(servletContext: ServletContext?) {
        insertFilters(servletContext, MultipartFilter())
    }
}

XML 構成の Spring Security フィルターの前に MultipartFilter を指定するために、ユーザーは、以下に示すように、web.xml 内の springSecurityFilterChain の前に MultipartFilter の <filter-mapping> エレメントを配置することができます。

web.xml - MultipartFilter
<filter>
	<filter-name>MultipartFilter</filter-name>
	<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter>
	<filter-name>springSecurityFilterChain</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>MultipartFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
	<filter-name>springSecurityFilterChain</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

URL に CSRF トークンを含める

認可されていないユーザーによる一時ファイルのアップロードを認可しない場合は、Spring Security フィルターの後に MultipartFilter を配置し、フォームのアクション属性にクエリパラメーターとして CSRF を含めることもできます。CsrfToken は HttpServletRequest  リクエスト属性として公開されているため、それを使用して CSRF トークンを含む action を作成できます。jsp を使用した例を以下に示します

実行中の CSRF トークン
<form method="post"
	action="./upload?${_csrf.parameterName}=${_csrf.token}"
	enctype="multipart/form-data">

HiddenHttpMethodFilter

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

Spring のサーブレットサポートでは、HiddenHttpMethodFilter (Javadoc) を使用して HTTP メソッドをオーバーライドします。詳細については、リファレンスドキュメントの HTTP メソッド変換セクションを参照してください。