Add reactive opt out steps for CSRF BREACH

Issue gh-11959
This commit is contained in:
Steve Riesenberg 2022-11-19 22:10:59 -06:00
parent 4994e67eda
commit 4442a618ea
No known key found for this signature in database
GPG Key ID: 5F311AB48A55D521
1 changed files with 113 additions and 3 deletions

View File

@ -32,7 +32,7 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
[source,kotlin,role="secondary"]
----
@Bean
open fun securityWebFilterChain(http: HttpSecurity): SecurityWebFilterChain {
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http {
// ...
csrf {
@ -67,7 +67,7 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
[source,kotlin,role="secondary"]
----
@Bean
open fun securityWebFilterChain(http: HttpSecurity): SecurityWebFilterChain {
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
val requestHandler = ServerCsrfTokenRequestAttributeHandler()
requestHandler.tokenFromMultipartDataEnabled = true
return http {
@ -106,7 +106,7 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
[source,kotlin,role="secondary"]
----
@Bean
open fun securityWebFilterChain(http: HttpSecurity): SecurityWebFilterChain {
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
val requestHandler = XorServerCsrfTokenRequestAttributeHandler()
// ...
return http {
@ -119,6 +119,116 @@ open fun securityWebFilterChain(http: HttpSecurity): SecurityWebFilterChain {
----
====
[[reactive-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 `ServerCsrfTokenRequestHandler` with delegation, like so:
.Configure `CsrfToken` BREACH Protection to validate raw tokens
====
.Java
[source,java,role="primary"]
----
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
CookieServerCsrfTokenRepository tokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse();
XorServerCsrfTokenRequestAttributeHandler delegate = new XorServerCsrfTokenRequestAttributeHandler();
// Use only the handle() method of XorServerCsrfTokenRequestAttributeHandler and the
// default implementation of resolveCsrfTokenValue() from ServerCsrfTokenRequestHandler
ServerCsrfTokenRequestHandler requestHandler = delegate::handle;
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRepository(tokenRepository)
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
@Bean
WebFilter csrfCookieWebFilter() {
return (exchange, chain) -> {
Mono<CsrfToken> csrfToken = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());
return csrfToken.doOnSuccess(token -> {
/* Ensures the token is subscribed to. */
}).then(chain.filter(exchange));
};
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
val tokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse()
val delegate = XorServerCsrfTokenRequestAttributeHandler()
// Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
// default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
val requestHandler = ServerCsrfTokenRequestHandler(delegate::handle)
return http.invoke {
// ...
csrf {
csrfTokenRepository = tokenRepository
csrfTokenRequestHandler = requestHandler
}
}
}
@Bean
fun csrfCookieWebFilter(): WebFilter {
return WebFilter { exchange, chain ->
val csrfToken = exchange.getAttribute<Mono<CsrfToken>>(CsrfToken::class.java.name) ?: Mono.empty()
csrfToken.doOnSuccess { }.then(chain.filter(exchange))
}
}
----
====
==== 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 following configuration:
.Opt out of `CsrfToken` BREACH protection
====
.Java
[source,java,role="primary"]
----
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
ServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();
http
// ...
.csrf((csrf) -> csrf
.csrfTokenRequestHandler(requestHandler)
);
return http.build();
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
val requestHandler = ServerCsrfTokenRequestAttributeHandler()
return http {
// ...
csrf {
csrfTokenRequestHandler = requestHandler
}
}
}
----
====
== Use `AuthorizationManager` for Method Security
xref:reactive/authorization/method.adoc[Method Security] has been xref:reactive/authorization/method.adoc#jc-enable-reactive-method-security-authorization-manager[improved] through {security-api-url}org/springframework/security/authorization/AuthorizationManager.html[the `AuthorizationManager` API] and direct use of Spring AOP.