对于最新的稳定版本,请使用 Spring Security 6.3.3! |
对于最新的稳定版本,请使用 Spring Security 6.3.3! |
以下步骤与有关如何配置 CSRF 的更改有关。
延迟加载 CsrfToken
在 Spring Security 5 中,默认行为是将在每个请求上加载。
这意味着在典型设置中,必须为每个请求读取 the,即使它不是必需的。CsrfToken
HttpSession
一些不需要读取会话的示例包括标记的端点,例如静态资产、静态 HTML 页面、托管在同一域/服务器下的单页应用程序等。 |
在 Spring Security 6 中,默认情况下,将延迟到需要时再查找。CsrfToken
每当使用会更改应用程序状态的 HTTP 动词发出请求时,都需要 The 。
安全方法必须为只读中对此进行了详细介绍。
此外,任何将令牌呈现到响应的请求都需要它,例如带有标签的网页包含 CSRF 令牌的 hidden。 |
要选择新的 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"/>
当延迟时(Spring Security 6 中的默认值),某些应用程序可能会中断,因为它们是使用非延迟 CSRF 令牌设计的。
有关更多信息,请参阅下面的选择退出步骤。 |
选择退出步骤
如果配置 to be deferred 给您带来麻烦,请查看以下场景以实现最佳选择退出行为:CsrfToken
我正在使用单页应用程序CookieCsrfTokenRepository
如果您使用单页应用程序 (SPA) 连接到受 Spring Security 保护的后端以及 ,您可能会发现 CSRF 令牌不再作为 cookie 返回到您的应用程序的第一个请求到服务器。CookieCsrfTokenRepository.withHttpOnlyFalse()
在这种情况下,您有多种选择来恢复客户端应用程序预期的行为。
一种选择是添加一个,无论先发出哪个请求,它都会急切地呈现到响应中,如下所示:Filter
CsrfToken
Filter
-
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)
}
}
上述选项不需要更改单页应用程序,但确实会导致在每个请求上加载 。
如果您不希望在每个请求上都添加 a 以预先加载令牌,下面列出了其他选项。CsrfToken
Filter
我正在使用单页应用程序HttpSessionCsrfTokenRepository
如果您使用的是会话,则您的应用程序将受益于延迟令牌。
除了选择退出之外,另一个选项是添加具有终端节点的 new,如下所示:@RestController
/csrf
/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 令牌只加载一次,并且令牌可以继续延迟。
这种方法特别适用于使用延迟令牌并从中受益的应用程序,因为允许在每个请求上都不读取 the。 |
如果您只是希望完全退出延迟令牌,则接下来会列出该选项。
出于另一个原因,我需要选择退出延迟令牌
如果延迟令牌由于其他原因破坏了您的应用程序,则可以使用以下配置显式选择加入 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>
通过设置 to ,必须首先加载 以确定要使用的属性名称。
这会导致 to 在每个请求上加载。 |
一些不需要读取会话的示例包括标记的端点,例如静态资产、静态 HTML 页面、托管在同一域/服务器下的单页应用程序等。 |
每当使用会更改应用程序状态的 HTTP 动词发出请求时,都需要 The 。
安全方法必须为只读中对此进行了详细介绍。
此外,任何将令牌呈现到响应的请求都需要它,例如带有标签的网页包含 CSRF 令牌的 hidden。 |
当延迟时(Spring Security 6 中的默认值),某些应用程序可能会中断,因为它们是使用非延迟 CSRF 令牌设计的。
有关更多信息,请参阅下面的选择退出步骤。 |
如果在向服务器进行身份验证之前需要上述终端节点,您可以考虑添加。 |
在应用程序启动时调用终端节点的说明特定于您的客户端框架,因此不在本文档的讨论范围之内。 |
虽然这需要更改您的单页应用程序,但好处是 CSRF 令牌只加载一次,并且令牌可以继续延迟。
这种方法特别适用于使用延迟令牌并从中受益的应用程序,因为允许在每个请求上都不读取 the。 |
通过设置 to ,必须首先加载 以确定要使用的属性名称。
这会导致 to 在每个请求上加载。 |
防止 CSRF BREACH
如果延迟加载 CsrfToken 的步骤适合您,那么您还可以使用以下配置选择加入 Spring Security 6 对 BREACH 保护的默认支持:CsrfToken
CsrfToken
BREACH 保护-
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()
在这种情况下,你可以配置 Spring Security 来验证 cookie 中的原始数据,同时使用带有委托的自定义来保持响应的 CSRF BREACH 保护,如下所示:CsrfToken
CsrfTokenRequestHandler
CsrfToken
-
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"/>
这是将 Spring Security 配置为与使用 cookie 值的客户端应用程序一起使用的推荐方法,因为它继续允许响应返回 CSRF 令牌的随机值,以防应用程序返回 HTML 或其他可能在您不知情的情况下容易受到 BREACH 攻击的响应。
当令牌包含在可 GZIP 压缩的响应正文中时,BREACH 保护的作用是保护令牌,该响应正文通常不包括标头和 Cookie。 |
客户端应用程序可以成功使用服务器返回的任何令牌值,因为底层(原始)CSRF 令牌不会更改。 AngularJS(或类似)应用程序不需要在每个请求之前/之后刷新 CSRF 令牌。 |
如果您只是想完全退出 CSRF BREACH 保护,那么接下来会列出该选项。
出于另一个原因,我需要选择退出 CSRF BREACH 保护
如果 CSRF BREACH 保护由于其他原因对您不起作用,您可以使用 Defer Loading CsrfToken
部分中的配置选择退出。
当令牌包含在可 GZIP 压缩的响应正文中时,BREACH 保护的作用是保护令牌,该响应正文通常不包括标头和 Cookie。 |
客户端应用程序可以成功使用服务器返回的任何令牌值,因为底层(原始)CSRF 令牌不会更改。 AngularJS(或类似)应用程序不需要在每个请求之前/之后刷新 CSRF 令牌。 |
支持 WebSocket 的 CSRF BREACH
如果防止 CSRF BREACH 的步骤适用于普通的 HTTP 请求,并且您使用的是 WebSocket 安全支持,那么您还可以选择加入 Spring Security 6 对带有 Stomp 标头的 BREACH 保护的默认支持。CsrfToken
-
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"/>