最新の安定バージョンについては、Spring Security 6.4.5 を使用してください! |
サーブレット環境のクロスサイトリクエストフォージェリ(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 で構成できます。
<http>
<!-- ... -->
<csrf token-repository-ref="tokenRepository"/>
</http>
<b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
p:cookieHttpOnly="false"/>
サンプルは |
以下を使用して、Java 構成で CookieCsrfTokenRepository
を構成できます。
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()
}
}
サンプルは |
CSRF 保護を無効にする
CSRF 保護はデフォルトで有効になっています。ただし、アプリケーションにとって意味がある場合は、CSRF 保護を無効にするのは簡単です。
以下の XML 構成は、CSRF 保護を無効にします。
<http>
<!-- ... -->
<csrf disabled="true"/>
</http>
以下の Java 構成は、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 は次のようになります。
<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 でこれを実行する例を以下に示します。
<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 は次のようになります。
<html>
<head>
<meta name="_csrf" content="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
<meta name="_csrf_header" content="X-CSRF-TOKEN"/>
<!-- ... -->
</head>
<!-- ... -->
メタタグに CSRF トークンが含まれると、JavaScript コードはメタタグを読み取り、CSRF トークンをヘッダーとして含めます。jQuery を使用している場合は、次のように実行できます。
$(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 でこれを実行する例を以下に示します。
<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 でログアウトを実行します。
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
をカスタマイズする方法の例については、xml と Java 構成 [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 をオーバーライドできます。
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 構成で MultipartFilter
が Spring Security フィルターの前に指定されるようにするには、次に示すように、web.xml 内で MultipartFilter
の <filter-mapping> 要素が springSecurityFilterChain の前に配置されていることを確認します。
<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 を使用した例を以下に示します
<form method="post"
action="./upload?${_csrf.parameterName}=${_csrf.token}"
enctype="multipart/form-data">
HiddenHttpMethodFilter
CSRF トークンを本体に配置することのトレードオフについてはすでに説明しました。
Spring のサーブレットサポートでは、HiddenHttpMethodFilter (Javadoc) を使用して HTTP メソッドをオーバーライドします。詳細については、リファレンスドキュメントの HTTP メソッド変換セクションを参照してください。