| 对于最新的稳定版本,请使用 Spring Security 6.5.3! | 
漏洞利用保护迁移
以下步骤与如何配置 CSRF 的更改有关。
延迟加载 CsrfToken
在 Spring Security 5 中,默认行为是CsrfToken将加载到每个请求中。
这意味着在典型设置中,HttpSession必须为每个请求读取,即使它没有必要。
| 不需要读取会话的一些示例包括标记为 | 
在 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 Tokens不再在向服务器发出第一次请求时作为 cookie 返回到您的应用程序。
在这种情况下,有多个选项可用于还原客户端应用程序预期的行为。
一种选择是添加一个Filter急切地渲染CsrfToken到响应,无论先发出哪个请求,如下所示:
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为了在每个请求上急切地加载Tokens,下面列出了其他选项。
我正在使用带有HttpSessionCsrfTokenRepository
如果您使用的是会话,您的应用程序将受益于延迟Tokens。
另一种选择是添加新的@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 Tokens只加载一次,并且可以继续延迟Tokens。
这种方法特别适用于使用 | 
如果您只是想完全选择退出延迟Tokens,则接下来列出了该选项。
出于其他原因,我需要选择退出延迟Tokens
如果延迟Tokens由于其他原因导致应用程序中断,则可以使用以下配置显式选择加入 5.8 默认值:
CsrfToken默认值为 5.8- 
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 BREACH
如果延迟加载 CsrfToken 的步骤适合您,那么您还可以选择加入 Spring Security 6 对 BREACH 保护的默认支持CsrfToken使用以下配置:
CsrfTokenBREACH 保护- 
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 来验证原始CsrfToken从 cookie 中获取,同时使用自定义CsrfTokenRequestHandler与委托,如下所示:
CsrfTokenBREACH 保护来验证原始Tokens- 
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值的客户端应用程序的推荐方法,因为它继续允许响应返回CSRFTokens的随机值,以防应用程序返回HTML或其他可能在您不知情的情况下容易受到BREACH攻击的响应。
| BREACH 保护用于保护Tokens,当Tokens包含在可以进行 GZIP 压缩的响应正文中时,该响应正文通常不包括标头和 cookie。 | 
| 服务器返回的任何Tokens值都可以被客户端应用程序成功使用,因为底层(原始)CSRF Tokens不会更改。 AngularJS(或类似)应用程序不需要在每次请求之前/之后刷新 CSRF Tokens。 | 
如果您只是想完全选择退出 CSRF BREACH 保护,则接下来列出了该选项。
出于其他原因,我需要选择退出 CSRF BREACH 保护
如果 CSRF BREACH 保护由于其他原因不适用于您,您可以使用延迟加载CsrfToken部分。
支持 WebSocket 的 CSRF BREACH
如果 Protect against CSRF BREACH 的步骤适用于正常的 HTTP 请求,并且您正在使用 WebSocket Security 支持,那么您还可以选择加入 Spring Security 6 对 BREACH 保护的默认支持CsrfToken带有 Stomp 标头。
- 
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 Security 配置 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"/>