From 4994e67eda1dcdebc19d744934ca1f061490d352 Mon Sep 17 00:00:00 2001 From: Steve Riesenberg Date: Sat, 19 Nov 2022 22:08:19 -0600 Subject: [PATCH 1/2] Add servlet opt out steps for CSRF BREACH Issue gh-12107 --- .../pages/migration/servlet/exploits.adoc | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/docs/modules/ROOT/pages/migration/servlet/exploits.adoc b/docs/modules/ROOT/pages/migration/servlet/exploits.adoc index d1472fe229..0e41a8a263 100644 --- a/docs/modules/ROOT/pages/migration/servlet/exploits.adoc +++ b/docs/modules/ROOT/pages/migration/servlet/exploits.adoc @@ -11,6 +11,7 @@ In Spring Security 6, the default is that the lookup of the `CsrfToken` will be 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 @@ -166,3 +167,79 @@ open fun springSecurity(http: HttpSecurity): SecurityFilterChain { 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"] +---- + + + + + +---- +==== + +==== 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 <> section. From 4442a618ea524df9678f1363ca799ec1e835244f Mon Sep 17 00:00:00 2001 From: Steve Riesenberg Date: Sat, 19 Nov 2022 22:10:59 -0600 Subject: [PATCH 2/2] Add reactive opt out steps for CSRF BREACH Issue gh-11959 --- .../ROOT/pages/migration/reactive.adoc | 116 +++++++++++++++++- 1 file changed, 113 insertions(+), 3 deletions(-) diff --git a/docs/modules/ROOT/pages/migration/reactive.adoc b/docs/modules/ROOT/pages/migration/reactive.adoc index 5c189cfb70..2ca5d33c72 100644 --- a/docs/modules/ROOT/pages/migration/reactive.adoc +++ b/docs/modules/ROOT/pages/migration/reactive.adoc @@ -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 = 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>(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.