Skip to content

Commit 4442a61

Browse files
author
Steve Riesenberg
committed
Add reactive opt out steps for CSRF BREACH
Issue gh-11959
1 parent 4994e67 commit 4442a61

File tree

1 file changed

+113
-3
lines changed

1 file changed

+113
-3
lines changed

docs/modules/ROOT/pages/migration/reactive.adoc

+113-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
3232
[source,kotlin,role="secondary"]
3333
----
3434
@Bean
35-
open fun securityWebFilterChain(http: HttpSecurity): SecurityWebFilterChain {
35+
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
3636
return http {
3737
// ...
3838
csrf {
@@ -67,7 +67,7 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
6767
[source,kotlin,role="secondary"]
6868
----
6969
@Bean
70-
open fun securityWebFilterChain(http: HttpSecurity): SecurityWebFilterChain {
70+
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
7171
val requestHandler = ServerCsrfTokenRequestAttributeHandler()
7272
requestHandler.tokenFromMultipartDataEnabled = true
7373
return http {
@@ -106,7 +106,7 @@ SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
106106
[source,kotlin,role="secondary"]
107107
----
108108
@Bean
109-
open fun securityWebFilterChain(http: HttpSecurity): SecurityWebFilterChain {
109+
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
110110
val requestHandler = XorServerCsrfTokenRequestAttributeHandler()
111111
// ...
112112
return http {
@@ -119,6 +119,116 @@ open fun securityWebFilterChain(http: HttpSecurity): SecurityWebFilterChain {
119119
----
120120
====
121121

122+
[[reactive-csrf-breach-opt-out]]
123+
=== Opt-out Steps
124+
125+
If configuring CSRF BREACH protection gives you trouble, take a look at these scenarios for optimal opt out behavior:
126+
127+
==== I am using AngularJS or another Javascript framework
128+
129+
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.
130+
131+
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:
132+
133+
.Configure `CsrfToken` BREACH Protection to validate raw tokens
134+
====
135+
.Java
136+
[source,java,role="primary"]
137+
----
138+
@Bean
139+
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
140+
CookieServerCsrfTokenRepository tokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse();
141+
XorServerCsrfTokenRequestAttributeHandler delegate = new XorServerCsrfTokenRequestAttributeHandler();
142+
// Use only the handle() method of XorServerCsrfTokenRequestAttributeHandler and the
143+
// default implementation of resolveCsrfTokenValue() from ServerCsrfTokenRequestHandler
144+
ServerCsrfTokenRequestHandler requestHandler = delegate::handle;
145+
http
146+
// ...
147+
.csrf((csrf) -> csrf
148+
.csrfTokenRepository(tokenRepository)
149+
.csrfTokenRequestHandler(requestHandler)
150+
);
151+
152+
return http.build();
153+
}
154+
155+
@Bean
156+
WebFilter csrfCookieWebFilter() {
157+
return (exchange, chain) -> {
158+
Mono<CsrfToken> csrfToken = exchange.getAttributeOrDefault(CsrfToken.class.getName(), Mono.empty());
159+
return csrfToken.doOnSuccess(token -> {
160+
/* Ensures the token is subscribed to. */
161+
}).then(chain.filter(exchange));
162+
};
163+
}
164+
----
165+
166+
.Kotlin
167+
[source,kotlin,role="secondary"]
168+
----
169+
@Bean
170+
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
171+
val tokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse()
172+
val delegate = XorServerCsrfTokenRequestAttributeHandler()
173+
// Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
174+
// default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
175+
val requestHandler = ServerCsrfTokenRequestHandler(delegate::handle)
176+
return http.invoke {
177+
// ...
178+
csrf {
179+
csrfTokenRepository = tokenRepository
180+
csrfTokenRequestHandler = requestHandler
181+
}
182+
}
183+
}
184+
185+
@Bean
186+
fun csrfCookieWebFilter(): WebFilter {
187+
return WebFilter { exchange, chain ->
188+
val csrfToken = exchange.getAttribute<Mono<CsrfToken>>(CsrfToken::class.java.name) ?: Mono.empty()
189+
csrfToken.doOnSuccess { }.then(chain.filter(exchange))
190+
}
191+
}
192+
----
193+
====
194+
195+
==== I need to opt out of CSRF BREACH protection for another reason
196+
197+
If CSRF BREACH protection does not work for you for another reason, you can opt out using the following configuration:
198+
199+
.Opt out of `CsrfToken` BREACH protection
200+
====
201+
.Java
202+
[source,java,role="primary"]
203+
----
204+
@Bean
205+
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
206+
ServerCsrfTokenRequestAttributeHandler requestHandler = new ServerCsrfTokenRequestAttributeHandler();
207+
http
208+
// ...
209+
.csrf((csrf) -> csrf
210+
.csrfTokenRequestHandler(requestHandler)
211+
);
212+
return http.build();
213+
}
214+
----
215+
216+
.Kotlin
217+
[source,kotlin,role="secondary"]
218+
----
219+
@Bean
220+
open fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
221+
val requestHandler = ServerCsrfTokenRequestAttributeHandler()
222+
return http {
223+
// ...
224+
csrf {
225+
csrfTokenRequestHandler = requestHandler
226+
}
227+
}
228+
}
229+
----
230+
====
231+
122232
== Use `AuthorizationManager` for Method Security
123233

124234
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.

0 commit comments

Comments
 (0)