You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
8.7 KiB
307 lines
8.7 KiB
= 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. |
|
|
|
== CSRF BREACH with WebSocket support |
|
|
|
If the steps for <<Protect against CSRF BREACH>> work for normal HTTP requests and you are using xref:servlet/integrations/websocket.adoc[WebSocket Security] support, then you can also opt into Spring Security 6's default support for BREACH protection of the `CsrfToken` with xref:servlet/integrations/websocket.adoc#websocket-sameorigin-csrf[Stomp headers]. |
|
|
|
.WebSocket Security BREACH Protection |
|
==== |
|
.Java |
|
[source,java,role="primary"] |
|
---- |
|
@Bean |
|
ChannelInterceptor csrfChannelInterceptor() { |
|
return new XorCsrfChannelInterceptor(); |
|
} |
|
---- |
|
|
|
.Kotlin |
|
[source,kotlin,role="secondary"] |
|
---- |
|
@Bean |
|
open fun csrfChannelInterceptor(): ChannelInterceptor { |
|
return XorCsrfChannelInterceptor() |
|
} |
|
---- |
|
|
|
.XML |
|
[source,xml,role="secondary"] |
|
---- |
|
<b:bean id="csrfChannelInterceptor" |
|
class="org.springframework.security.messaging.web.csrf.XorCsrfChannelInterceptor"/> |
|
---- |
|
==== |
|
|
|
If configuring CSRF BREACH protection for WebSocket Security gives you trouble, you can configure the 5.8 default using the following configuration: |
|
|
|
.Configure WebSocket Security with 5.8 default |
|
==== |
|
.Java |
|
[source,java,role="primary"] |
|
---- |
|
@Bean |
|
ChannelInterceptor csrfChannelInterceptor() { |
|
return new CsrfChannelInterceptor(); |
|
} |
|
---- |
|
|
|
.Kotlin |
|
[source,kotlin,role="secondary"] |
|
---- |
|
@Bean |
|
open fun csrfChannelInterceptor(): ChannelInterceptor { |
|
return CsrfChannelInterceptor() |
|
} |
|
---- |
|
|
|
.XML |
|
[source,xml,role="secondary"] |
|
---- |
|
<b:bean id="csrfChannelInterceptor" |
|
class="org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor"/> |
|
---- |
|
====
|
|
|