最新の安定バージョンについては、Spring Security 6.3.1 を使用してください! |
Exploit Protection の移行
次の手順は、CSRF の設定方法に関する変更に関連しています。
CsrfToken ロードの据え置き
Spring Security 5 では、デフォルトの動作は、すべてのリクエストで CsrfToken
がロードされることです。これは、通常のセットアップでは、不要な場合でも、リクエストごとに HttpSession
を読み取る必要があることを意味します。
セッションを読み取る必要のない場所の例には、静的アセット、静的 HTML ページ、同じドメイン / サーバーでホストされる単一ページアプリケーションなどの |
Spring Security 6 では、デフォルトでは、CsrfToken
のルックアップは必要になるまで延期されます。
|
新しい Spring Security 6 デフォルトを選択するには、次の構成を使用できます。
CsrfToken
Java
Kotlin
XML
@Bean
public SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName("_csrf");
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
@Bean
open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
val requestHandler = CsrfTokenRequestAttributeHandler()
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName("_csrf")
http {
csrf {
csrfTokenRequestHandler = requestHandler
}
}
return http.build()
}
<http>
<!-- ... -->
<csrf request-handler-ref="requestHandler"/>
</http>
<b:bean id="requestHandler"
class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler"
p:csrfRequestAttributeName="_csrf"/>
|
オプトアウトの手順
CsrfToken
を延期するように構成すると問題が発生する場合は、最適なオプトアウト動作について次のシナリオを参照してください。
CookieCsrfTokenRepository
でシングルページアプリケーションを使用しています
シングルページアプリ (SPA) を使用して Spring Security と CookieCsrfTokenRepository.withHttpOnlyFalse()
によって保護されているバックエンドに接続している場合、サーバーへの最初のリクエストで CSRF トークンが Cookie としてアプリケーションに返されなくなっていることがあります。
この場合、クライアント側アプリケーションが期待する動作を復元するためのオプションがいくつかあります。1 つのオプションは、次のように、どのリクエストが最初に行われたかに関係なく、CsrfToken
を積極的にレスポンスにレンダリングする Filter
を追加することです。
Filter
を追加して、レスポンスで Cookie を返す Java
Kotlin
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName("_csrf");
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRepository(tokenRepository)
.csrfTokenRequestHandler(requestHandler)
)
.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class);
return http.build();
}
private static final class CsrfCookieFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName());
// Render the token value to a cookie by causing the deferred token to be loaded
csrfToken.getToken();
filterChain.doFilter(request, response);
}
}
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
val tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse()
val requestHandler = CsrfTokenRequestAttributeHandler()
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName("_csrf")
http {
csrf {
csrfTokenRepository = tokenRepository
csrfTokenRequestHandler = requestHandler
}
addFilterAfter<BasicAuthenticationFilter>(CsrfCookieFilter())
}
return http.build()
}
class CsrfCookieFilter : OncePerRequestFilter() {
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
val csrfToken = request.getAttribute(CsrfToken::class.java.name) as CsrfToken
// Render the token value to a cookie by causing the deferred token to be loaded
csrfToken.token
filterChain.doFilter(request, response)
}
}
上記のオプションでは、単一ページのアプリケーションを変更する必要はありませんが、リクエストごとに CsrfToken
が読み込まれます。Filter
を追加してすべてのリクエストでトークンを積極的にロードしたくない場合は、追加のオプションを以下に示します。
HttpSessionCsrfTokenRepository
でシングルページアプリケーションを使用しています
セッションを使用している場合、アプリケーションは遅延トークンの恩恵を受けます。オプトアウトする代わりに、次のように /csrf
エンドポイントを持つ新しい @RestController
を追加するという別のオプションがあります。
/csrf
エンドポイントを追加する Java
Kotlin
@RestController
public class CsrfController {
@GetMapping("/csrf")
public CsrfToken csrf(CsrfToken csrfToken) {
return csrfToken;
}
}
@RestController
class CsrfController {
@GetMapping("/csrf")
fun csrf(csrfToken: CsrfToken): CsrfToken {
return csrfToken
}
}
サーバーで認証する前に上記のエンドポイントが必要な場合は、 |
/csrf
エンドポイントは、後続のリクエストのためにアプリケーションをブートストラップするために、クライアント側アプリケーションによって消費される必要があります。
アプリケーションの起動時に |
これには単一ページのアプリケーションを変更する必要がありますが、利点は、CSRF トークンが 1 回だけ読み込まれ、トークンを引き続き延期できることです。このアプローチは、 |
延期されたトークンを完全にオプトアウトしたいだけの場合は、そのオプションが次にリストされています。
別の理由で遅延トークンをオプトアウトする必要がある
据え置きトークンが別の理由でアプリケーションを破損する場合は、次の構成を使用して明示的に 5.8 デフォルトを選択できます。
CsrfToken
を明示的に設定する Java
Kotlin
XML
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName(null);
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
val requestHandler = CsrfTokenRequestAttributeHandler()
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName(null)
http {
csrf {
csrfTokenRequestHandler = requestHandler
}
}
return http.build()
}
<http>
<!-- ... -->
<csrf request-handler-ref="requestHandler"/>
</http>
<b:bean id="requestHandler"
class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler">
<b:property name="csrfRequestAttributeName">
<b:null/>
</b:property>
</b:bean>
|
CSRF 違反からの保護
CsrfToken ロードの据え置きの手順がうまくいく場合は、次の構成を使用して、CsrfToken
の BREACH 保護に対する Spring Security 6 のデフォルトサポートを選択することもできます。
CsrfToken
違反保護 Java
Kotlin
XML
@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
XorCsrfTokenRequestAttributeHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName("_csrf");
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
@Bean
open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
val requestHandler = XorCsrfTokenRequestAttributeHandler()
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName("_csrf")
http {
csrf {
csrfTokenRequestHandler = requestHandler
}
}
return http.build()
}
<http>
<!-- ... -->
<csrf request-handler-ref="requestHandler"/>
</http>
<b:bean id="requestHandler"
class="org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler"
p:csrfRequestAttributeName="_csrf"/>
オプトアウトの手順
CSRF BREACH 保護を構成すると問題が発生する場合は、次のシナリオを参照して、最適なオプトアウト動作を確認してください。
AngularJS または別の Javascript フレームワークを使用しています
AngularJS と HttpClientXsrfModule (英語) (または別のフレームワークの同様のモジュール) を CookieCsrfTokenRepository.withHttpOnlyFalse()
と共に使用している場合、自動サポートが機能しなくなることがあります。
この場合、次のように、委譲のあるカスタム CsrfTokenRequestHandler
を使用して、レスポンスの CSRF BREACH 保護を維持しながら、Cookie から生の CsrfToken
を検証するように Spring Security を構成できます。
CsrfToken
BREACH Protection を構成する Java
Kotlin
XML
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
// set the name of the attribute the CsrfToken will be populated on
delegate.setCsrfRequestAttributeName("_csrf");
// Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
// default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
CsrfTokenRequestHandler requestHandler = delegate::handle;
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRepository(tokenRepository)
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
@Bean
open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
val tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse()
val delegate = XorCsrfTokenRequestAttributeHandler()
// set the name of the attribute the CsrfToken will be populated on
delegate.setCsrfRequestAttributeName("_csrf")
// Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
// default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
val requestHandler = CsrfTokenRequestHandler(delegate::handle)
http {
csrf {
csrfTokenRepository = tokenRepository
csrfTokenRequestHandler = requestHandler
}
}
return http.build()
}
<http>
<!-- ... -->
<csrf token-repository-ref="tokenRepository"
request-handler-ref="requestHandler"/>
</http>
<b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
p:cookieHttpOnly="false"/>
これは、Cookie 値を使用するクライアント側アプリケーションで動作するように Spring Security を構成するための推奨される方法です。これは、アプリケーションが脆弱である可能性のある HTML またはその他のレスポンスを返した場合にレスポンスが CSRF トークンのランダム化された値を返すことを引き続き許可するためです。あなたの知らないうちに違反します。
違反保護は、GZIP 圧縮可能なレスポンス本文にトークンが含まれている場合に、トークンを保護するために機能します。これには通常、ヘッダーと Cookie は含まれません。 |
基になる (生の) CSRF トークンは変更されないため、サーバーから返されたトークン値はクライアント側アプリケーションで正常に使用できます。AngularJS (または同様の) アプリケーションがすべてのリクエストの前後に CSRF トークンをリフレッシュする必要はありません。 |
CSRF BREACH 保護を完全にオプトアウトしたいだけの場合は、そのオプションが次にリストされています。
別の理由で CSRF BREACH 保護をオプトアウトする必要があります
CSRF BREACH 保護が別の理由で機能しない場合は、遅延ロード CsrfToken
セクションの構成を使用してオプトアウトできます。
WebSocket をサポートする CSRF 違反
CSRF 違反からの保護の手順が通常の HTTP リクエストで機能し、WebSocket セキュリティサポートを使用している場合は、ストンプヘッダーを使用した CsrfToken
のブリーチ保護に対する Spring Security 6 のデフォルトサポートを選択することもできます。
Java
Kotlin
XML
@Bean
ChannelInterceptor csrfChannelInterceptor() {
return new XorCsrfChannelInterceptor();
}
@Bean
open fun csrfChannelInterceptor(): ChannelInterceptor {
return XorCsrfChannelInterceptor()
}
<b:bean id="csrfChannelInterceptor"
class="org.springframework.security.messaging.web.csrf.XorCsrfChannelInterceptor"/>
WebSocket セキュリティの CSRF BREACH 保護を構成すると問題が発生する場合は、次の構成を使用して 5.8 のデフォルトを構成できます。
Java
Kotlin
XML
@Bean
ChannelInterceptor csrfChannelInterceptor() {
return new CsrfChannelInterceptor();
}
@Bean
open fun csrfChannelInterceptor(): ChannelInterceptor {
return CsrfChannelInterceptor()
}
<b:bean id="csrfChannelInterceptor"
class="org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor"/>