Cross Site Request Forgery (CSRF)
在最终用户可以 log in的应用程序中,重要的是考虑如何防御 Cross Site Request Forgery (CSRF)。
In an application where end users can log in, it is important to consider how to protect against Cross Site Request Forgery (CSRF).
Spring Security 默认情况下会对针对 unsafe HTTP methods的 CSRF 攻击(如 POST 请求)提供保护,因此无需额外的代码。您可以使用以下内容明确指定默认配置:
Spring Security protects against CSRF attacks by default for unsafe HTTP methods, such as a POST request, so no additional code is necessary. You can specify the default configuration explicitly using the following:
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.csrf(Customizer.withDefaults());
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
csrf { }
}
return http.build()
}
}
<http>
<!-- ... -->
<csrf/>
</http>
要详细了解应用程序的 CSRF 防御,请考虑以下使用案例:
To learn more about CSRF protection for your application, consider the following use cases:
-
I want to csrf-components
-
I need to migrating-to-spring-security-6
-
I want to csrf-token-repository-cookie instead of csrf-token-repository-httpsession
-
I want to csrf-token-repository-custom
-
I want to deferred-csrf-token-opt-out
-
I need guidance integrating JSPs or another view technology,csrf-integration-form with the backend
-
I need guidance integrating csrf-integration-javascript with the backend
-
I need guidance integrating csrf-integration-mobile with the backend
-
I need guidance on csrf-access-denied-handler
-
I want to csrf-testing
-
I need guidance on disable-csrf
Understanding CSRF Protection’s Components
CSRF 保护由 {security-api-url}org/springframework/security/web/csrf/CsrfFilter.html[CsrfFilter
] 中组合的多个组件提供:
CSRF protection is provided by several components that are composed within the {security-api-url}org/springframework/security/web/csrf/CsrfFilter.html[CsrfFilter
]:
CsrfFilter
ComponentsCSRF 防御分为两部分:
CSRF protection is divided into two parts:
-
Make the {security-api-url}org/springframework/security/web/csrf/CsrfToken.html[
CsrfToken
] available to the application by delegating to the <<`CsrfTokenRequestHandler`,csrf-token-request-handler>>. -
Determine if the request requires CSRF protection, load and validate the token, and csrf-access-denied-handler.
CsrfFilter
Processing-
First, the {security-api-url}org/springframework/security/web/csrf/DeferredCsrfToken.html[
DeferredCsrfToken
] is loaded, which holds a reference to the <<`CsrfTokenRepository`,csrf-token-repository>> so that the persistedCsrfToken
can be loaded later (in ). -
Second, a
Supplier<CsrfToken>
(created fromDeferredCsrfToken
) is given to the <<`CsrfTokenRequestHandler`,csrf-token-request-handler>>, which is responsible for populating a request attribute to make theCsrfToken
available to the rest of the application. -
Next, the main CSRF protection processing begins and checks if the current request requires CSRF protection. If not required, the filter chain is continued and processing ends.
-
If CSRF protection is required, the persisted
CsrfToken
is finally loaded from theDeferredCsrfToken
. -
Continuing, the actual CSRF token provided by the client (if any) is resolved using the <<`CsrfTokenRequestHandler`,csrf-token-request-handler>>.
-
The actual CSRF token is compared against the persisted
CsrfToken
. If valid, the filter chain is continued and processing ends. -
If the actual CSRF token is invalid (or missing), an
AccessDeniedException
is passed to the <<`AccessDeniedHandler`,csrf-access-denied-handler>> and processing ends.
Migrating to Spring Security 6
从 Spring Security 5 迁移到 6 时,有一些更改可能会影响您的应用程序。以下是针对 Spring Security 6 中更改的 CSRF 保护方面的概述:
When migrating from Spring Security 5 to 6, there are a few changes that may impact your application. The following is an overview of the aspects of CSRF protection that have changed in Spring Security 6:
-
Loading of the
CsrfToken
is now deferred-csrf-token to improve performance by no longer requiring the session to be loaded on every request. -
The
CsrfToken
now includes csrf-token-request-handler-breach to protect the CSRF token from a BREACH attack.
Spring Security 6 中的更改需要对单页应用程序进行额外的配置,因此您可能觉得 Single-Page Applications 一节特别有用。 The changes in Spring Security 6 require additional configuration for single-page applications, and as such you may find the Single-Page Applications section particularly useful. |
有关迁移 Spring Security 5 应用程序的更多信息,请参阅 Migration 一章的 Exploit Protection 部分。
See the Exploit Protection section of the Migration chapter for more information on migrating a Spring Security 5 application.
Persisting the CsrfToken
CsrfToken
使用 CsrfTokenRepository
进行持久化。
The CsrfToken
is persisted using a CsrfTokenRepository
.
默认情况下,<<`HttpSessionCsrfTokenRepository`,csrf-token-repository-httpsession>> 用于将标记存储在会话中。Spring Security 还提供 <<`CookieCsrfTokenRepository`,csrf-token-repository-cookie>> 用于将标记存储在 Cookie 中。您还可以指定 your own implementation 以便根据需要随时存储标记。
By default, the <<`HttpSessionCsrfTokenRepository`,csrf-token-repository-httpsession>> is used for storing tokens in a session. Spring Security also provides the <<`CookieCsrfTokenRepository`,csrf-token-repository-cookie>> for storing tokens in a cookie. You can also specify csrf-token-repository-custom to store tokens wherever you like.
Using the HttpSessionCsrfTokenRepository
默认情况下,Spring Security 使用 {security-api-url}org/springframework/security/web/csrf/HttpSessionCsrfTokenRepository.html[HttpSessionCsrfTokenRepository
] 将预期的 CSRF 令牌存储在 HttpSession
中,因此无需其他代码。
By default, Spring Security stores the expected CSRF token in the HttpSession
by using {security-api-url}org/springframework/security/web/csrf/HttpSessionCsrfTokenRepository.html[HttpSessionCsrfTokenRepository
], so no additional code is necessary.
HttpSessionCsrfTokenRepository
默认情况下从名为 X-CSRF-TOKEN
的 HTTP 请求标头或请求参数 _csrf
读入标记。
The HttpSessionCsrfTokenRepository
reads the token from an HTTP request header named X-CSRF-TOKEN
or the request parameter _csrf
by default.
您可以使用以下配置明确指定默认配置:
You can specify the default configuration explicitly using the following configuration:
HttpSessionCsrfTokenRepository
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRepository(new HttpSessionCsrfTokenRepository())
);
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
csrf {
csrfTokenRepository = HttpSessionCsrfTokenRepository()
}
}
return http.build()
}
}
<http>
<!-- ... -->
<csrf token-repository-ref="tokenRepository"/>
</http>
<b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository"/>
Using the CookieCsrfTokenRepository
您可以使用 {security-api-url}org/springframework/security/web/csrf/CookieCsrfTokenRepository.html[CookieCsrfTokenRepository
] 将 CsrfToken
保存在 cookie 中以 support a JavaScript-based application。
You can persist the CsrfToken
in a cookie to csrf-integration-javascript using the {security-api-url}org/springframework/security/web/csrf/CookieCsrfTokenRepository.html[CookieCsrfTokenRepository
].
CookieCsrfTokenRepository
默认情况下会写入一个名为 XSRF-TOKEN
的 cookie,并从一个名为 X-XSRF-TOKEN
的 HTTP 请求头或一个请求参数 _csrf
中读取它。这些默认值来自 Angular 及其前身 AngularJS。
The CookieCsrfTokenRepository
writes to a cookie named XSRF-TOKEN
and reads it from an HTTP request header named X-XSRF-TOKEN
or the request parameter _csrf
by default.
These defaults come from Angular and its predecessor AngularJS.
有关此主题的最新信息,请参阅 Cross-Site Request Forgery (XSRF) protection 指南和 HttpClientXsrfModule。 See the Cross-Site Request Forgery (XSRF) protection guide and the HttpClientXsrfModule for more recent information on this topic. |
您可以使用以下配置来配置 CookieCsrfTokenRepository
:
You can configure the CookieCsrfTokenRepository
using the following configuration:
CookieCsrfTokenRepository
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
);
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
csrf {
csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse()
}
}
return http.build()
}
}
<http>
<!-- ... -->
<csrf token-repository-ref="tokenRepository"/>
</http>
<b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
p:cookieHttpOnly="false"/>
该示例明确将 The example explicitly sets |
Customizing the CsrfTokenRepository
在某些情况下,您可能需要实现一个自定义 {security-api-url}org/springframework/security/web/csrf/CsrfTokenRepository.html[CsrfTokenRepository
]。
There can be cases where you want to implement a custom {security-api-url}org/springframework/security/web/csrf/CsrfTokenRepository.html[CsrfTokenRepository
].
一旦实现了 CsrfTokenRepository
接口,您可以将 Spring Security 配置为使用以下配置:
Once you’ve implemented the CsrfTokenRepository
interface, you can configure Spring Security to use it with the following configuration:
CsrfTokenRepository
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRepository(new CustomCsrfTokenRepository())
);
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
csrf {
csrfTokenRepository = CustomCsrfTokenRepository()
}
}
return http.build()
}
}
<http>
<!-- ... -->
<csrf token-repository-ref="tokenRepository"/>
</http>
<b:bean id="tokenRepository"
class="example.CustomCsrfTokenRepository"/>
Handling the CsrfToken
CsrfToken
通过 CsrfTokenRequestHandler
提供给应用程序。此组件还负责从 HTTP 标头或请求参数中解析 CsrfToken
。
The CsrfToken
is made available to an application using a CsrfTokenRequestHandler
.
This component is also responsible for resolving the CsrfToken
from HTTP headers or request parameters.
默认情况下,<<`XorCsrfTokenRequestAttributeHandler`,csrf-token-request-handler-breach>> 用于提供 CsrfToken
的 BREACH 保护。Spring Security 还提供了 csrf-token-request-handler-plain 来选择退出 BREACH 保护。您还可以指定 your own implementation 来自定义处理和解析令牌的策略。
By default, the <<`XorCsrfTokenRequestAttributeHandler`,csrf-token-request-handler-breach>> is used for providing BREACH protection of the CsrfToken
.
Spring Security also provides the <<`CsrfTokenRequestAttributeHandler`,csrf-token-request-handler-plain>> for opting out of BREACH protection.
You can also specify csrf-token-request-handler-custom to customize the strategy for handling and resolving tokens.
Using the XorCsrfTokenRequestAttributeHandler
(BREACH)
XorCsrfTokenRequestAttributeHandler
使 CsrfToken
可用,作为一个名为 _csrf
的 HttpServletRequest
属性,并为 BREACH 提供保护。
The XorCsrfTokenRequestAttributeHandler
makes the CsrfToken
available as an HttpServletRequest
attribute called _csrf
, and additionally provides protection for BREACH.
The |
此实现还将标记值从请求中解析为请求标头(默认情况下为 <<`X-CSRF-TOKEN`, csrf-token-repository-httpsession>> 或 <<`X-XSRF-TOKEN`, csrf-token-repository-cookie>>)或请求参数(默认情况下为 _csrf
)。
This implementation also resolves the token value from the request as either a request header (one of <<`X-CSRF-TOKEN`,csrf-token-repository-httpsession>> or <<`X-XSRF-TOKEN`,csrf-token-repository-cookie>> by default) or a request parameter (_csrf
by default).
BREACH 保护通过将随机性编码到 CSRF 令牌值中来确保返回的 BREACH protection is provided by encoding randomness into the CSRF token value to ensure the returned |
默认情况下,Spring Security 会保护 CSRF 令牌免受 BREACH 攻击,因此无需其他代码。您可以使用以下配置明确指定默认配置:
Spring Security protects the CSRF token from a BREACH attack by default, so no additional code is necessary. You can specify the default configuration explicitly using the following configuration:
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(new XorCsrfTokenRequestAttributeHandler())
);
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
csrf {
csrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
}
}
return http.build()
}
}
<http>
<!-- ... -->
<csrf request-handler-ref="requestHandler"/>
</http>
<b:bean id="requestHandler"
class="org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler"/>
Using the CsrfTokenRequestAttributeHandler
CsrfTokenRequestAttributeHandler
使 CsrfToken
作为名为 _csrf
的 HttpServletRequest
属性提供。
The CsrfTokenRequestAttributeHandler
makes the CsrfToken
available as an HttpServletRequest
attribute called _csrf
.
The |
此实现还将标记值从请求中解析为请求标头(默认情况下为 <<`X-CSRF-TOKEN`, csrf-token-repository-httpsession>> 或 <<`X-XSRF-TOKEN`, csrf-token-repository-cookie>>)或请求参数(默认情况下为 _csrf
)。
This implementation also resolves the token value from the request as either a request header (one of <<`X-CSRF-TOKEN`,csrf-token-repository-httpsession>> or <<`X-XSRF-TOKEN`,csrf-token-repository-cookie>> by default) or a request parameter (_csrf
by default).
CsrfTokenRequestAttributeHandler
的主要用途是退出 CsrfToken
的 BREACH 保护,可以使用以下配置进行配置:
The primary use of CsrfTokenRequestAttributeHandler
is to opt-out of BREACH protection of the CsrfToken
, which can be configured using the following configuration:
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
);
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
csrf {
csrfTokenRequestHandler = CsrfTokenRequestAttributeHandler()
}
}
return http.build()
}
}
<http>
<!-- ... -->
<csrf request-handler-ref="requestHandler"/>
</http>
<b:bean id="requestHandler"
class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler"/>
Customizing the CsrfTokenRequestHandler
您可以实现 CsrfTokenRequestHandler
接口以自定义处理和解析令牌的策略。
You can implement the CsrfTokenRequestHandler
interface to customize the strategy for handling and resolving tokens.
The |
一旦实现了 CsrfTokenRequestHandler
接口,您可以将 Spring Security 配置为使用以下配置:
Once you’ve implemented the CsrfTokenRequestHandler
interface, you can configure Spring Security to use it with the following configuration:
CsrfTokenRequestHandler
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(new CustomCsrfTokenRequestHandler())
);
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
csrf {
csrfTokenRequestHandler = CustomCsrfTokenRequestHandler()
}
}
return http.build()
}
}
<http>
<!-- ... -->
<csrf request-handler-ref="requestHandler"/>
</http>
<b:bean id="requestHandler"
class="example.CustomCsrfTokenRequestHandler"/>
Deferred Loading of the CsrfToken
默认情况下,Spring Security 会延迟加载 CsrfToken
,直至需要为止。
By default, Spring Security defers loading of the CsrfToken
until it is needed.
无论何时通过 unsafe HTTP method(如 POST)进行请求,都需要 The |
由于 Spring Security 默认情况下还将 CsrfToken
存储在 HttpSession
中,因此延迟 CSRF 令牌可以通过不需要在每个请求中加载会话来提高性能。
Because Spring Security also stores the CsrfToken
in the HttpSession
by default, deferred CSRF tokens can improve performance by not requiring the session to be loaded on every request.
如果您希望选择退出延迟令牌并导致 CsrfToken
在每个请求中加载,可以使用以下配置:
In the event that you want to opt-out of deferred tokens and cause the CsrfToken
to be loaded on every request, you can do so with the following configuration:
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
XorCsrfTokenRequestAttributeHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();
// set the name of the attribute the CsrfToken will be populated on
requestHandler.setCsrfRequestAttributeName(null);
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
val requestHandler = XorCsrfTokenRequestAttributeHandler()
// 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>
通过将 By setting the |
Integrating with CSRF Protection
为了使 synchronizer token pattern免受 CSRF 攻击,我们必须在 HTTP 请求中包含实际的 CSRF 令牌。这必须包含在请求的一部分(表单参数、HTTP 头或其他部分)中,浏览器不会自动将它包含在 HTTP 请求中。
For the synchronizer token pattern to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request. This must be included in a part of the request (a form parameter, an HTTP header, or other part) that is not automatically included in the HTTP request by the browser.
下列部分描述了前端或客户端应用程序可以与受 CSRF 保护的后端应用程序集成的各种方式:
The following sections describe the various ways a frontend or client application can integrate with a CSRF-protected backend application:
HTML Forms
要提交 HTML 表单,CSRF 令牌必须作为隐藏输入包含在表单中。例如,呈现的 HTML 可能如下所示:
To submit an HTML form, the CSRF token must be included in the form as a hidden input. For example, the rendered HTML might look like:
<input type="hidden"
name="_csrf"
value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
以下视图技术会自动在具有不安全 HTTP 方法(例如 POST)的表单中包含实际的 CSRF 令牌:
The following view technologies automatically include the actual CSRF token in a form that has an unsafe HTTP method, such as a POST:
-
Any other view technology that integrates with {spring-framework-api-url}org/springframework/web/servlet/support/RequestDataValueProcessor.html[
RequestDataValueProcessor
] (via {security-api-url}org/springframework/security/web/servlet/support/csrf/CsrfRequestDataValueProcessor.html[CsrfRequestDataValueProcessor
]) -
You can also include the token yourself via the csrfInput tag
如果这些选项不可用,您可以利用以下事实:SecurityContextHolder
作为名为 csrf-token-request-handler
的 HttpServletRequest
属性公开。以下示例通过 JSP 这样做:
If these options are not available, you can take advantage of the fact that the CsrfToken
is exposed as an <<`HttpServletRequest` attribute named _csrf
,csrf-token-request-handler>>.
The following example does this with a 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>
JavaScript Applications
JavaScript 应用程序通常使用 JSON 而不是 HTML。如果您使用 JSON,您可以通过 HTTP 请求头(而不是请求参数)提交 CSRF 令牌。
JavaScript applications typically use JSON instead of HTML. If you use JSON, you can submit the CSRF token within an HTTP request header instead of a request parameter.
为了获得 CSRF 令牌,您可以配置 Spring Security 来存储预期的 CSRF 令牌。通过将预期令牌存储在 cookie 中,JavaScript 框架(例如 jQuery)可以自动将实际 CSRF 令牌作为 HTTP 请求头包含进来。
In order to obtain the CSRF token, you can configure Spring Security to store the expected CSRF token csrf-token-repository-cookie. By storing the expected token in a cookie, JavaScript frameworks such as Angular can automatically include the actual CSRF token as an HTTP request header.
当将单页面应用程序 (SPA) 与 Spring Security 的 CSRF 保护集成时,需要特别考虑 BREACH 保护和延迟令牌。在中提供了完整的配置示例。 There are special considerations for BREACH protection and deferred tokens when integrating a single-page application (SPA) with Spring Security’s CSRF protection. A full configuration example is provided in the csrf-integration-javascript-spa. |
您可以在以下部分中了解不同类型的 JavaScript 应用程序:
You can read about different types of JavaScript applications in the following sections:
Single-Page Applications
将单页面应用程序 (SPA) 与 Spring Security 的 CSRF 保护集成时需要特别考虑。
There are special considerations for integrating a single-page application (SPA) with Spring Security’s CSRF protection.
请记住,Spring Security 默认提供 CSRF 保护。当存储预期的 CSRF 令牌时,JavaScript 应用程序只能访问纯令牌值,而不能访问编码值。需要提供一个解析实际令牌值的解析器。
Recall that Spring Security provides csrf-token-request-handler-breach by default. When storing the expected CSRF token csrf-token-repository-cookie, JavaScript applications will only have access to the plain token value and will not have access to the encoded value. A csrf-token-request-handler-custom for resolving the actual token value will need to be provided.
此外,存储 CSRF 令牌的 cookie 将在身份验证成功和注销成功后被清除。Spring Security 默认延迟加载新的 CSRF 令牌,并且需要额外的操作来返回一个新的 cookie。
In addition, the cookie storing the CSRF token will be cleared upon authentication success and logout success. Spring Security defers loading a new CSRF token by default, and additional work is required to return a fresh cookie.
在身份验证成功和注销成功后刷新令牌是必需的,因为 Refreshing the token after authentication success and logout success is required because the {security-api-url}org/springframework/security/web/csrf/CsrfAuthenticationStrategy.html[ |
为了轻松地将单页面应用程序与 Spring Security 集成,可以使用以下配置:
In order to easily integrate a single-page application with Spring Security, the following configuration can be used:
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) (1)
.csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()) (2)
)
.addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class); (3)
return http.build();
}
}
final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler {
private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler();
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
/*
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
* the CsrfToken when it is rendered in the response body.
*/
this.delegate.handle(request, response, csrfToken);
}
@Override
public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
/*
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
* to resolve the CsrfToken. This applies when a single-page application includes
* the header value automatically, which was obtained via a cookie containing the
* raw CsrfToken.
*/
if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) {
return super.resolveCsrfTokenValue(request, csrfToken);
}
/*
* In all other cases (e.g. if the request contains a request parameter), use
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
* when a server-side rendered form includes the _csrf request parameter as a
* hidden input.
*/
return this.delegate.resolveCsrfTokenValue(request, csrfToken);
}
}
final class CsrfCookieFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
// Render the token value to a cookie by causing the deferred token to be loaded
csrfToken.getToken();
filterChain.doFilter(request, response);
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
csrf {
csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse() (1)
csrfTokenRequestHandler = SpaCsrfTokenRequestHandler() (2)
}
}
http.addFilterAfter(CsrfCookieFilter(), BasicAuthenticationFilter::class.java) (3)
return http.build()
}
}
class SpaCsrfTokenRequestHandler : CsrfTokenRequestAttributeHandler() {
private val delegate: CsrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
override fun handle(request: HttpServletRequest, response: HttpServletResponse, csrfToken: Supplier<CsrfToken>) {
/*
* Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
* the CsrfToken when it is rendered in the response body.
*/
delegate.handle(request, response, csrfToken)
}
override fun resolveCsrfTokenValue(request: HttpServletRequest, csrfToken: CsrfToken): String {
/*
* If the request contains a request header, use CsrfTokenRequestAttributeHandler
* to resolve the CsrfToken. This applies when a single-page application includes
* the header value automatically, which was obtained via a cookie containing the
* raw CsrfToken.
*/
return if (StringUtils.hasText(request.getHeader(csrfToken.headerName))) {
super.resolveCsrfTokenValue(request, csrfToken)
} else {
/*
* In all other cases (e.g. if the request contains a request parameter), use
* XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
* when a server-side rendered form includes the _csrf request parameter as a
* hidden input.
*/
delegate.resolveCsrfTokenValue(request, csrfToken)
}
}
}
class CsrfCookieFilter : OncePerRequestFilter() {
@Throws(ServletException::class, IOException::class)
override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
val csrfToken = request.getAttribute("_csrf") as CsrfToken
// Render the token value to a cookie by causing the deferred token to be loaded
csrfToken.token
filterChain.doFilter(request, response)
}
}
<http>
<!-- ... -->
<csrf
token-repository-ref="tokenRepository" 1
request-handler-ref="requestHandler"/> 2
<custom-filter ref="csrfCookieFilter" after="BASIC_AUTH_FILTER"/> 3
</http>
<b:bean id="tokenRepository"
class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
p:cookieHttpOnly="false"/>
<b:bean id="requestHandler"
class="example.SpaCsrfTokenRequestHandler"/>
<b:bean id="csrfCookieFilter"
class="example.CsrfCookieFilter"/>
1 | Configure CookieCsrfTokenRepository with HttpOnly set to false so the cookie can be read by the JavaScript application. |
2 | Configure a custom CsrfTokenRequestHandler that resolves the CSRF token based on whether it is an HTTP request header (X-XSRF-TOKEN ) or request parameter (_csrf ). |
3 | Configure a custom Filter to load the CsrfToken on every request, which will return a new cookie if needed. |
Multi-Page Applications
对于在每个页面加载 JavaScript 的多页面应用程序,除了公开 CSRF 令牌之外的另一种选择是在 meta
标签中包含 CSRF 令牌。HTML 可能如下所示:
For multi-page applications where JavaScript is loaded on each page, an alternative to exposing the CSRF token csrf-token-repository-cookie is to include the CSRF token within your meta
tags.
The HTML might look something like this:
<html>
<head>
<meta name="_csrf" content="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
<meta name="_csrf_header" content="X-CSRF-TOKEN"/>
<!-- ... -->
</head>
<!-- ... -->
</html>
为了在请求中包含 CSRF 令牌,您可以利用以下事实:SecurityContextHolder
作为名为 csrf-token-request-handler
的 HttpServletRequest
属性公开。以下示例通过 JSP 这样做:
In order to include the CSRF token in the request, you can take advantage of the fact that the CsrfToken
is exposed as an <<`HttpServletRequest` attribute named _csrf
,csrf-token-request-handler>>.
The following example does this with a JSP:
<html>
<head>
<meta name="_csrf" content="${_csrf.token}"/>
<!-- default header name is X-CSRF-TOKEN -->
<meta name="_csrf_header" content="${_csrf.headerName}"/>
<!-- ... -->
</head>
<!-- ... -->
</html>
一旦元标记包含了 CSRF 令牌,JavaScript 代码就可以读取元标记并将 CSRF 令牌作为一个标头包含进来。如果您使用 jQuery,您可以使用以下代码这样做:
Once the meta tags contain the CSRF token, the JavaScript code can read the meta tags and include the CSRF token as a header. If you use jQuery, you can do this with the following code:
$(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);
});
});
Other JavaScript Applications
JavaScript 应用程序的另一个选项是将 CSRF 令牌包含在 HTTP 响应头中。
Another option for JavaScript applications is to include the CSRF token in an HTTP response header.
实现此目的的一种方法是使用带有 CsrfTokenArgumentResolver
的 @ControllerAdvice
。以下是适用于应用程序中所有控制器端点的 `@ControllerAdvice`示例:
One way to achieve this is through the use of a @ControllerAdvice
with the CsrfTokenArgumentResolver
.
The following is an example of @ControllerAdvice
that applies to all controller endpoints in the application:
-
Java
-
Kotlin
@ControllerAdvice
public class CsrfControllerAdvice {
@ModelAttribute
public void getCsrfToken(HttpServletResponse response, CsrfToken csrfToken) {
response.setHeader(csrfToken.getHeaderName(), csrfToken.getToken());
}
}
@ControllerAdvice
class CsrfControllerAdvice {
@ModelAttribute
fun getCsrfToken(response: HttpServletResponse, csrfToken: CsrfToken) {
response.setHeader(csrfToken.headerName, csrfToken.token)
}
}
因为这个注释适用于应用程序中的所有端点,所以它将导致在每个请求中加载 CSRF 令牌,而当使用 Because this |
请记住,控制器端点和控制器建议在 Spring 安全过滤器链中称为 after。这意味着该 It is important to remember that controller endpoints and controller advice are called after the Spring Security filter chain.
This means that this |
CSR 令牌现在将可在对任何自定义端点的控制器建议适用的响应头中获取(<<`X-CSRF-TOKEN`,csrf-token-repository-httpsession>> 或 <<`X-XSRF-TOKEN`,csrf-token-repository-cookie>>,默认为)。可使用对后端的任何请求从响应中获取令牌,而后续请求可将令牌包含在名称相同的请求头中。
The CSRF token will now be available in a response header (<<`X-CSRF-TOKEN`,csrf-token-repository-httpsession>> or <<`X-XSRF-TOKEN`,csrf-token-repository-cookie>> by default) for any custom endpoints the controller advice applies to. Any request to the backend can be used to obtain the token from the response, and a subsequent request can include the token in a request header with the same name.
Mobile Applications
与 JavaScript applications 一样,移动应用程序通常使用 JSON,而不是 HTML。一个 does not 服务浏览器流量的后端应用程序可能会选择 disable CSRF。在这种情况下,不需要任何其他工作。
Like csrf-integration-javascript, mobile applications typically use JSON instead of HTML. A backend application that does not serve browser traffic may choose to disable-csrf. In that case, no additional work is required.
但是,一个既服务浏览器流量又因此 still requires CSR 保护的后端应用程序可能仍继续存储 CsrfToken
in the session,而不是 in a cookie。
However, a backend application that also serves browser traffic and therefore still requires CSRF protection may continue to store the CsrfToken
csrf-token-repository-httpsession instead of csrf-token-repository-cookie.
在这种情况下,与后端集成的典型模式是公开一个 /csrf
端点,供前端(移动或浏览器客户端)按需请求 CSR 令牌。使用此模式的好处是,CSR 令牌 can continue to be deferred 仅在请求需要 CSR 保护时才需要从会话中加载。使用自定义端点还意味着客户端应用程序可以通过发出明确的请求按需请求生成新令牌(如果需要)。
In this case, a typical pattern for integrating with the backend is to expose a /csrf
endpoint to allow the frontend (mobile or browser client) to request a CSRF token on demand.
The benefit of using this pattern is that the CSRF token deferred-csrf-token and only needs to be loaded from the session when a request requires CSRF protection.
The use of a custom endpoint also means the client application can request that a new token be generated on demand (if necessary) by issuing an explicit request.
此模式可用于需要 CSR 保护的任何类型应用程序,而不仅仅是移动应用程序。虽然在这些情况下通常不需要这种方法,但它是与 CSR 受到保护的后端集成的另一种选择。 This pattern can be used for any type of application that requires CSRF protection, not just mobile applications. While this approach isn’t typically required in those cases, it is another option for integrating with a CSRF-protected backend. |
以下是使用 CsrfTokenArgumentResolver
的 `/csrf`端点的示例:
The following is an example of the /csrf
endpoint that makes use of the CsrfTokenArgumentResolver
:
/csrf
endpoint-
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
}
}
如果在向服务器进行身份验证之前需要上述端点,你可以考虑添加 You may consider adding |
该端点应在应用程序启动或初始化(例如,在加载时)时调用以获取 CSR 令牌,还应在身份验证成功和注销成功后调用。
This endpoint should be called to obtain a CSRF token when the application is launched or initialized (e.g. at load time), and also after authentication success and logout success.
在身份验证成功和注销成功后刷新令牌是必需的,因为 Refreshing the token after authentication success and logout success is required because the {security-api-url}org/springframework/security/web/csrf/CsrfAuthenticationStrategy.html[ |
一旦你获取了 CSR 令牌,你需要将其自身作为 HTTP 请求头(默认为 <<`X-CSRF-TOKEN`,csrf-token-repository-httpsession>> 或 <<`X-XSRF-TOKEN`,csrf-token-repository-cookie>> 之一)包含在内。
Once you’ve obtained the CSRF token, you will need to include it as an HTTP request header (one of <<`X-CSRF-TOKEN`,csrf-token-repository-httpsession>> or <<`X-XSRF-TOKEN`,csrf-token-repository-cookie>> by default) yourself.
Handle AccessDeniedException
为了处理诸如 InvalidCsrfTokenException
之类的 AccessDeniedException
,你可以配置 Spring Security 以任意方式处理这些异常。例如,你可以使用以下配置来配置自定义的拒绝访问页面:
To handle an AccessDeniedException
such as InvalidCsrfTokenException
, you can configure Spring Security to handle these exceptions in any way you like.
For example, you can configure a custom access denied page using the following configuration:
AccessDeniedHandler
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.exceptionHandling((exceptionHandling) -> exceptionHandling
.accessDeniedPage("/access-denied")
);
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
exceptionHandling {
accessDeniedPage = "/access-denied"
}
}
return http.build()
}
}
<http>
<!-- ... -->
<access-denied-handler error-page="/access-denied"/>
</http>
CSRF Testing
您可以使用 Spring Security 的 testing support 和 CsrfRequestPostProcessor
来测试 CSRF 保护,如下所示:
You can use Spring Security’s testing support and CsrfRequestPostProcessor
to test CSRF protection, like this:
-
Java
-
Kotlin
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = SecurityConfig.class)
@WebAppConfiguration
public class CsrfTests {
private MockMvc mockMvc;
@BeforeEach
public void setUp(WebApplicationContext applicationContext) {
this.mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext)
.apply(springSecurity())
.build();
}
@Test
public void loginWhenValidCsrfTokenThenSuccess() throws Exception {
this.mockMvc.perform(post("/login").with(csrf())
.accept(MediaType.TEXT_HTML)
.param("username", "user")
.param("password", "password"))
.andExpect(status().is3xxRedirection())
.andExpect(header().string(HttpHeaders.LOCATION, "/"));
}
@Test
@WithMockUser
public void logoutWhenValidCsrfTokenThenSuccess() throws Exception {
this.mockMvc.perform(post("/logout").with(csrf())
.accept(MediaType.TEXT_HTML))
.andExpect(status().is3xxRedirection())
.andExpect(header().string(HttpHeaders.LOCATION, "/login?logout"));
}
}
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*
import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
@ExtendWith(SpringExtension::class)
@ContextConfiguration(classes = [SecurityConfig::class])
@WebAppConfiguration
class CsrfTests {
private lateinit var mockMvc: MockMvc
@BeforeEach
fun setUp(applicationContext: WebApplicationContext) {
mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext)
.apply<DefaultMockMvcBuilder>(springSecurity())
.build()
}
@Test
fun loginWhenValidCsrfTokenThenSuccess() {
mockMvc.perform(post("/login").with(csrf())
.accept(MediaType.TEXT_HTML)
.param("username", "user")
.param("password", "password"))
.andExpect(status().is3xxRedirection)
.andExpect(header().string(HttpHeaders.LOCATION, "/"))
}
@Test
@WithMockUser
@Throws(Exception::class)
fun logoutWhenValidCsrfTokenThenSuccess() {
mockMvc.perform(post("/logout").with(csrf())
.accept(MediaType.TEXT_HTML))
.andExpect(status().is3xxRedirection)
.andExpect(header().string(HttpHeaders.LOCATION, "/login?logout"))
}
}
Disable CSRF Protection
默认情况下,启用 CSRF 保护,这会影响应用程序的 integrating with the backend和 testing。在禁用 CSRF 保护之前,请考虑是否 makes sense for your application。
By default, CSRF protection is enabled, which affects csrf-integration and csrf-testing your application. Before disabling CSRF protection, consider whether it makes sense for your application.
你还可以考虑是否只有某些端点不需要 CSR 保护并配置一个忽略规则,如下图例所示:
You can also consider whether only certain endpoints do not require CSRF protection and configure an ignoring rule, as in the following example:
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.csrf((csrf) -> csrf
.ignoringRequestMatchers("/api/*")
);
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
csrf {
ignoringRequestMatchers("/api/*")
}
}
return http.build()
}
}
<http>
<!-- ... -->
<csrf request-matcher-ref="csrfMatcher"/>
</http>
<b:bean id="csrfMatcher"
class="org.springframework.security.web.util.matcher.AndRequestMatcher">
<b:constructor-arg value="#{T(org.springframework.security.web.csrf.CsrfFilter).DEFAULT_CSRF_MATCHER}"/>
<b:constructor-arg>
<b:bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
<b:bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
<b:constructor-arg value="/api/*"/>
</b:bean>
</b:bean>
</b:constructor-arg>
</b:bean>
如果你需要禁用 CSR 保护,你可以使用以下配置进行禁用:
If you need to disable CSRF protection, you can do so using the following configuration:
-
Java
-
Kotlin
-
XML
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.csrf((csrf) -> csrf.disable());
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
csrf {
disable()
}
}
return http.build()
}
}
<http>
<!-- ... -->
<csrf disabled="true"/>
</http>
CSRF Considerations
在实现针对 CSRF 攻击的保护时,有一些特殊注意事项。本节讨论了这些注意事项,因为它们与 servlet 环境有关。有关更一般的讨论,请参阅 CSRF Considerations。
There are a few special considerations when implementing protection against CSRF attacks. This section discusses those considerations as they pertain to servlet environments. See CSRF Considerations for a more general discussion.
Logging In
require CSRF for log in请求对于保护伪造登录尝试非常重要。Spring Security 对 servlet 的支持开箱即用地实现了这一点。
It is important to require CSRF for log in requests to protect against forging log in attempts. Spring Security’s servlet support does this out of the box.
Logging Out
require CSRF for log out请求对于防御伪造注销尝试非常重要。如果启用 CSRF 保护(默认),Spring Security 的 `LogoutFilter`将只处理 HTTP POST 请求。这可确保注销需要 CSRF 令牌,并且恶意用户无法强制注销您的用户。
It is important to require CSRF for log out requests to protect against forging logout attempts.
If CSRF protection is enabled (the default), Spring Security’s LogoutFilter
will only process HTTP POST requests.
This ensures that logging out requires a CSRF token and that a malicious user cannot forcibly log your users out.
最简单的方法是使用表单来注销用户。如果你真的想要一个链接,你可以使用 JavaScript 使链接执行 POST(可能在隐藏的表单上)。对于禁用 JavaScrip 的浏览器,你可以选择让链接将用户带到执行 POST 的注销确认页面。
The easiest approach is to use a form to log the user out. If you really want a link, you can use JavaScript to have the link perform a POST (maybe on a hidden form). For browsers with JavaScript that is disabled, you can optionally have the link take the user to a log out confirmation page that performs the POST.
如果你真的想使用 HTTP GET 注销,你可以这么做。但是,请记住,通常不建议这样做。例如,以下内容会在使用任何 HTTP 方法请求 /logout
URL 时注销:
If you really want to use HTTP GET with logout, you can do so.
However, remember that this is generally not recommended.
For example, the following logs out when the /logout
URL is requested with any HTTP method:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.logout((logout) -> logout
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
);
return http.build();
}
}
import org.springframework.security.config.annotation.web.invoke
@Configuration
@EnableWebSecurity
class SecurityConfig {
@Bean
open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
// ...
logout {
logoutRequestMatcher = AntPathRequestMatcher("/logout")
}
}
return http.build()
}
}
有关更多信息,请参阅Logout章节。
See the Logout chapter for more information.
CSRF and Session Timeouts
默认情况下,Spring Security 将 CSRF 令牌存储在`HttpSession`中,使用<<`HttpSessionCsrfTokenRepository`,csrf-token-repository-httpsession>>。这会导致这样一个情况:会话过期,没有 CSRF 令牌进行验证。
By default, Spring Security stores the CSRF token in the HttpSession
using the <<`HttpSessionCsrfTokenRepository`,csrf-token-repository-httpsession>>.
This can lead to a situation where the session expires, leaving no CSRF token to validate against.
我们在 general solutions 讨论了会话超时。该部分讨论了 CSRF 超时的具体内容,因为它与服务支持有关。
We have already discussed general solutions to session timeouts. This section discusses the specifics of CSRF timeouts as it pertains to the servlet support.
你可以将 CSRF 令牌存储更改为 cookie 中。有关详细信息,请参阅Using the CookieCsrfTokenRepository
一节。
You can change the storage of the CSRF token to be in a cookie.
For details, see the Using the CookieCsrfTokenRepository
section.
如果令牌确实过期,你可能希望通过指定一个custom AccessDeniedHandler
来自定义处理方式。自定义`AccessDeniedHandler`可以任意处理`InvalidCsrfTokenException`。
If a token does expire, you might want to customize how it is handled by specifying a csrf-access-denied-handler.
The custom AccessDeniedHandler
can process the InvalidCsrfTokenException
any way you like.
Multipart (file upload)
我们 already discussed 如何保护 multipart 请求(文件上传)免受 CSRF 攻击导致 chicken and the egg 问题。当可以使用 JavaScript 时,我们 recommend including the CSRF token in an HTTP request header 来回避该问题。
We have already discussed how protecting multipart requests (file uploads) from CSRF attacks causes a chicken and the egg problem. When JavaScript is available, we recommend csrf-integration-javascript-other to side-step the issue.
If JavaScript is not available, the following sections discuss options for placing the CSRF token in the csrf-considerations-multipart-body and csrf-considerations-multipart-url within a servlet application.
你可以在 Spring 参考的 Multipart Resolver部分和{spring-framework-api-url}org/springframework/web/multipart/support/MultipartFilter.html[ You can find more information about using multipart forms with Spring in the Multipart Resolver section of the Spring reference and the {spring-framework-api-url}org/springframework/web/multipart/support/MultipartFilter.html[ |
Place CSRF Token in the Body
我们 already discussed 了将 CSRF 令牌放置在正文中的权衡。在该部分中,我们讨论如何配置 Spring Security 从正文读取 CSRF。
We have already discussed the tradeoffs of placing the CSRF token in the body. In this section, we discuss how to configure Spring Security to read the CSRF from the body.
要从 body 读取 CSRF 令牌,需要在 Spring Security 过滤器之前指定`MultipartFilter`。在 Spring Security 过滤器之前指定`MultipartFilter`意味着没有对调用`MultipartFilter`的授权,这意味着任何人都可以将临时文件放置在你的服务器上。但是,只有授权用户才能提交你的应用程序处理的文件。通常情况下,这是推荐的方法,因为临时文件上传对大多数服务器的影响微乎其微。
To read the CSRF token from the body, the MultipartFilter
is specified before the Spring Security filter.
Specifying the MultipartFilter
before the Spring Security filter means that there is no authorization for invoking the MultipartFilter
, which means anyone can place temporary files on your server.
However, only authorized users can submit a file that is processed by your application.
In general, this is the recommended approach because the temporary file upload should have a negligible impact on most servers.
MultipartFilter
-
Java
-
Kotlin
-
XML
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())
}
}
<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>
为了确保在 XML 配置中将 To ensure that |
Include a CSRF Token in a URL
如果允许未授权用户上传临时文件不可接受,一种解决方法是将 MultipartFilter
放在 Spring Security 过滤器之后,并将 CSRF 作为查询参数包含在表单的 action 属性中。由于 CsrfToken
以名为 _csrf`的<<`HttpServletRequest`属性的形式公开,csrf-token-request-handler>>, 我们可以使用它来创建一个其中包含 CSRF 令牌的`action
。以下示例通过 JSP 来执行此操作:
If letting unauthorized users upload temporary files is not acceptable, an alternative is to place the MultipartFilter
after the Spring Security filter and include the CSRF as a query parameter in the action attribute of the form.
Since the CsrfToken
is exposed as an <<`HttpServletRequest` attribute named _csrf
,csrf-token-request-handler>>, we can use that to create an action
with the CSRF token in it.
The following example does this with a JSP:
<form method="post"
action="./upload?${_csrf.parameterName}=${_csrf.token}"
enctype="multipart/form-data">
HiddenHttpMethodFilter
我们已经 already discussed 讨论了将 CSRF 令牌放置在正文中的权衡。
We have already discussed the trade-offs of placing the CSRF token in the body.
在 Spring 的 Servlet 支持中,通过使用{spring-framework-api-url}org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[HiddenHttpMethodFilter
]来覆盖 HTTP 方法。你可以在参考文档的 HTTP Method Conversion部分找到更多信息。
In Spring’s Servlet support, overriding the HTTP method is done by using {spring-framework-api-url}org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[HiddenHttpMethodFilter
].
You can find more information in the HTTP Method Conversion section of the reference documentation.
Further Reading
现在你已经了解了 CSRF 防护,请考虑更多了解exploit protection,包括secure headers和HTTP firewall,或者继续了解如何test你的应用。
Now that you have reviewed CSRF protection, consider learning more about exploit protection including secure headers and the HTTP firewall or move on to learning how to test your application.