246 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
			
		
		
	
	
			246 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
= Exploit Protection Migrations
 | 
						|
 | 
						|
The following steps relate to changes around how to configure CSRF.
 | 
						|
 | 
						|
== Defer Loading CsrfToken
 | 
						|
 | 
						|
In Spring Security 5, the default behavior is that the `CsrfToken` will be loaded on every request.
 | 
						|
This means that in a typical setup, the `HttpSession` must be read for every request even if it is unnecessary.
 | 
						|
 | 
						|
In Spring Security 6, the default is that the lookup of the `CsrfToken` will be deferred until it is needed.
 | 
						|
 | 
						|
To opt into the new Spring Security 6 default, the following configuration can be used.
 | 
						|
 | 
						|
[[servlet-opt-in-defer-loading-csrf-token]]
 | 
						|
.Defer Loading `CsrfToken`
 | 
						|
====
 | 
						|
.Java
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
DefaultSecurityFilterChain 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();
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
.Kotlin
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@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()
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
.XML
 | 
						|
[source,xml,role="secondary"]
 | 
						|
----
 | 
						|
<http>
 | 
						|
	<!-- ... -->
 | 
						|
	<csrf request-handler-ref="requestHandler"/>
 | 
						|
</http>
 | 
						|
<b:bean id="requestHandler"
 | 
						|
	class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler"
 | 
						|
	p:csrfRequestAttributeName="_csrf"/>
 | 
						|
----
 | 
						|
====
 | 
						|
 | 
						|
If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
 | 
						|
 | 
						|
.Explicit Configure `CsrfToken` with 5.8 Defaults
 | 
						|
====
 | 
						|
.Java
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
DefaultSecurityFilterChain springSecurity(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();
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
.Kotlin
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
open fun springSecurity(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()
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
.XML
 | 
						|
[source,xml,role="secondary"]
 | 
						|
----
 | 
						|
<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>
 | 
						|
----
 | 
						|
====
 | 
						|
 | 
						|
== Protect against CSRF BREACH
 | 
						|
 | 
						|
If the steps for <<Defer Loading CsrfToken>> work for you, then you can also opt into Spring Security 6's default support for BREACH protection of the `CsrfToken` using the following configuration:
 | 
						|
 | 
						|
.`CsrfToken` BREACH Protection
 | 
						|
====
 | 
						|
.Java
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@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();
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
.Kotlin
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@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()
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
.XML
 | 
						|
[source,xml,role="secondary"]
 | 
						|
----
 | 
						|
<http>
 | 
						|
	<!-- ... -->
 | 
						|
	<csrf request-handler-ref="requestHandler"/>
 | 
						|
</http>
 | 
						|
<b:bean id="requestHandler"
 | 
						|
	class="org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler"
 | 
						|
	p:csrfRequestAttributeName="_csrf"/>
 | 
						|
----
 | 
						|
====
 | 
						|
 | 
						|
[[servlet-csrf-breach-opt-out]]
 | 
						|
=== Opt-out Steps
 | 
						|
 | 
						|
If configuring CSRF BREACH protection gives you trouble, take a look at these scenarios for optimal opt out behavior:
 | 
						|
 | 
						|
==== I am using AngularJS or another Javascript framework
 | 
						|
 | 
						|
If you are using AngularJS and the https://angular.io/api/common/http/HttpClientXsrfModule[HttpClientXsrfModule] (or a similar module in another framework) along with `CookieCsrfTokenRepository.withHttpOnlyFalse()`, you may find that automatic support no longer works.
 | 
						|
 | 
						|
In this case, you can configure Spring Security to validate the raw `CsrfToken` from the cookie while keeping CSRF BREACH protection of the response using a custom `CsrfTokenRequestHandler` with delegation, like so:
 | 
						|
 | 
						|
.Configure `CsrfToken` BREACH Protection to validate raw tokens
 | 
						|
====
 | 
						|
.Java
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@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();
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
.Kotlin
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@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()
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
.XML
 | 
						|
[source,xml,role="secondary"]
 | 
						|
----
 | 
						|
<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"/>
 | 
						|
----
 | 
						|
====
 | 
						|
 | 
						|
==== I need to opt out of CSRF BREACH protection for another reason
 | 
						|
 | 
						|
If CSRF BREACH protection does not work for you for another reason, you can opt out using the configuration from the <<servlet-opt-in-defer-loading-csrf-token>> section.
 |