| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453 | 
							- [[webflux-csrf]]
 
- = Cross Site Request Forgery (CSRF) for WebFlux Environments
 
- This section discusses Spring Security's xref:features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] support for WebFlux environments.
 
- [[webflux-csrf-using]]
 
- == Using Spring Security CSRF Protection
 
- The steps to using Spring Security's CSRF protection are outlined below:
 
- * <<webflux-csrf-idempotent,Use proper HTTP verbs>>
 
- * <<webflux-csrf-configure,Configure CSRF Protection>>
 
- * <<webflux-csrf-include,Include the CSRF Token>>
 
- [[webflux-csrf-idempotent]]
 
- === Use Proper HTTP Verbs
 
- The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs.
 
- This is covered in detail in xref:features/exploits/csrf.adoc#csrf-protection-idempotent[Safe Methods Must be Idempotent].
 
- [[webflux-csrf-configure]]
 
- === Configure CSRF Protection
 
- The next step is to configure Spring Security's CSRF protection within your application.
 
- By default, Spring Security's CSRF protection is enabled, but you may need to customize the configuration.
 
- The next few subsections cover a few common customizations.
 
- [[webflux-csrf-configure-custom-repository]]
 
- ==== Custom CsrfTokenRepository
 
- By default, Spring Security stores the expected CSRF token in the `WebSession` by using `WebSessionServerCsrfTokenRepository`.
 
- Sometimes, you may need to configure a custom `ServerCsrfTokenRepository`.
 
- For example, you may want to persist the `CsrfToken` in a cookie to <<webflux-csrf-include-ajax-auto,support a JavaScript-based application>>.
 
- By default, the `CookieServerCsrfTokenRepository` writes to a cookie named `XSRF-TOKEN` and read its from a header named `X-XSRF-TOKEN` or the HTTP `_csrf` parameter.
 
- These defaults come from https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS]
 
- You can configure `CookieServerCsrfTokenRepository` in Java Configuration:
 
- .Store CSRF Token in a Cookie
 
- ====
 
- .Java
 
- [source,java,role="primary"]
 
- -----
 
- @Bean
 
- public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
 
- 	http
 
- 		// ...
 
- 		.csrf(csrf -> csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()))
 
- 	return http.build();
 
- }
 
- -----
 
- .Kotlin
 
- [source,kotlin,role="secondary"]
 
- -----
 
- @Bean
 
- fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
 
-     return http {
 
-         // ...
 
-         csrf {
 
-             csrfTokenRepository = CookieServerCsrfTokenRepository.withHttpOnlyFalse()
 
-         }
 
-     }
 
- }
 
- -----
 
- ====
 
- [NOTE]
 
- ====
 
- The preceding sample explicitly sets `cookieHttpOnly=false`.
 
- This is necessary to let JavaScript (in this case, AngularJS) to read it.
 
- If you do not need the ability to read the cookie with JavaScript directly, we recommend to omitting `cookieHttpOnly=false` (by using `new CookieServerCsrfTokenRepository()` instead) to improve security.
 
- ====
 
- [[webflux-csrf-configure-disable]]
 
- ==== Disable CSRF Protection
 
- By default, CSRF protection is enabled.
 
- However, you can disable CSRF protection if it xref:features/exploits/csrf.adoc#csrf-when[makes sense for your application].
 
- The Java configuration below will disable CSRF protection.
 
- .Disable CSRF Configuration
 
- ====
 
- .Java
 
- [source,java,role="primary"]
 
- ----
 
- @Bean
 
- public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
 
- 	http
 
- 		// ...
 
- 		.csrf(csrf -> csrf.disable()))
 
- 	return http.build();
 
- }
 
- ----
 
- .Kotlin
 
- [source,kotlin,role="secondary"]
 
- -----
 
- @Bean
 
- fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
 
-     return http {
 
-         // ...
 
-         csrf {
 
-             disable()
 
-         }
 
-     }
 
- }
 
- -----
 
- ====
 
- [[webflux-csrf-configure-request-handler]]
 
- ==== Configure ServerCsrfTokenRequestHandler
 
- Spring Security's https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/server/csrf/CsrfWebFilter.html[`CsrfWebFilter`] exposes a https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/server/csrf/CsrfToken.html[`Mono<CsrfToken>`] as a `ServerWebExchange` attribute named `org.springframework.security.web.server.csrf.CsrfToken` with the help of a https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/server/csrf/ServerCsrfTokenRequestHandler.html[`ServerCsrfTokenRequestHandler`].
 
- In 5.8, the default implementation was `ServerCsrfTokenRequestAttributeHandler`, which simply makes the `Mono<CsrfToken>` available as an exchange attribute.
 
- As of 6.0, the default implementation is `XorServerCsrfTokenRequestAttributeHandler`, which provides protection for BREACH (see https://github.com/spring-projects/spring-security/issues/4001[gh-4001]).
 
- If you wish to disable BREACH protection of the `CsrfToken` and revert to the 5.8 default, you can configure `ServerCsrfTokenRequestAttributeHandler` using the following Java configuration:
 
- .Disable BREACH protection
 
- ====
 
- .Java
 
- [source,java,role="primary"]
 
- -----
 
- @Bean
 
- public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
 
- 	http
 
- 		// ...
 
- 		.csrf(csrf -> csrf
 
- 			.csrfTokenRequestHandler(new ServerCsrfTokenRequestAttributeHandler())
 
- 		)
 
- 	return http.build();
 
- }
 
- -----
 
- .Kotlin
 
- [source,kotlin,role="secondary"]
 
- -----
 
- @Bean
 
- fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
 
-     return http {
 
-         // ...
 
-         csrf {
 
-             csrfTokenRequestHandler = ServerCsrfTokenRequestAttributeHandler()
 
-         }
 
-     }
 
- }
 
- -----
 
- ====
 
- [[webflux-csrf-include]]
 
- === Include the CSRF Token
 
- For the xref:features/exploits/csrf.adoc#csrf-protection-stp[synchronizer token pattern] to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request.
 
- It must be included in a part of the request (a form parameter, an HTTP header, or other option) that is not automatically included in the HTTP request by the browser.
 
- <<webflux-csrf-configure-request-handler,We've seen>> that the `Mono<CsrfToken>` is exposed as a `ServerWebExchange` attribute.
 
- This means that any view technology can access the `Mono<CsrfToken>` to expose the expected token as either a <<webflux-csrf-include-form-attr,form>> or a <<webflux-csrf-include-ajax-meta,meta tag>>.
 
- [[webflux-csrf-include-subscribe]]
 
- If your view technology does not provide a simple way to subscribe to the `Mono<CsrfToken>`, a common pattern is to use Spring's `@ControllerAdvice` to expose the `CsrfToken` directly.
 
- The following example places the `CsrfToken` on the default attribute name (`_csrf`) used by Spring Security's <<webflux-csrf-include-form-auto,CsrfRequestDataValueProcessor>> to automatically include the CSRF token as a hidden input:
 
- .`CsrfToken` as `@ModelAttribute`
 
- ====
 
- .Java
 
- [source,java,role="primary"]
 
- ----
 
- @ControllerAdvice
 
- public class SecurityControllerAdvice {
 
- 	@ModelAttribute
 
- 	Mono<CsrfToken> csrfToken(ServerWebExchange exchange) {
 
- 		Mono<CsrfToken> csrfToken = exchange.getAttribute(CsrfToken.class.getName());
 
- 		return csrfToken.doOnSuccess(token -> exchange.getAttributes()
 
- 				.put(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME, token));
 
- 	}
 
- }
 
- ----
 
- .Kotlin
 
- [source,kotlin,role="secondary"]
 
- ----
 
- @ControllerAdvice
 
- class SecurityControllerAdvice {
 
-     @ModelAttribute
 
-     fun csrfToken(exchange: ServerWebExchange): Mono<CsrfToken> {
 
-         val csrfToken: Mono<CsrfToken>? = exchange.getAttribute(CsrfToken::class.java.name)
 
-         return csrfToken!!.doOnSuccess { token ->
 
-             exchange.attributes[CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME] = token
 
-         }
 
-     }
 
- }
 
- ----
 
- ====
 
- Fortunately, Thymeleaf provides <<webflux-csrf-include-form-auto,integration>> that works without any additional work.
 
- [[webflux-csrf-include-form]]
 
- ==== Form URL Encoded
 
- To post an HTML form, the CSRF token must be included in the form as a hidden input.
 
- The following example shows what the rendered HTML might look like:
 
- .CSRF Token HTML
 
- ====
 
- [source,html]
 
- ----
 
- <input type="hidden"
 
- 	name="_csrf"
 
- 	value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
 
- ----
 
- ====
 
- Next, we discuss various ways of including the CSRF token in a form as a hidden input.
 
- [[webflux-csrf-include-form-auto]]
 
- ===== Automatic CSRF Token Inclusion
 
- Spring Security's CSRF support provides integration with Spring's https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/result/view/RequestDataValueProcessor.html[`RequestDataValueProcessor`] through its https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/reactive/result/view/CsrfRequestDataValueProcessor.html[`CsrfRequestDataValueProcessor`].
 
- For `CsrfRequestDataValueProcessor` to work, the `Mono<CsrfToken>` must be subscribed to and the `CsrfToken` must be <<webflux-csrf-include-subscribe,exposed as an attribute>> that matches https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/reactive/result/view/CsrfRequestDataValueProcessor.html#DEFAULT_CSRF_ATTR_NAME[`DEFAULT_CSRF_ATTR_NAME`].
 
- Fortunately, Thymeleaf https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#integration-with-requestdatavalueprocessor[takes care of all the boilerplate] for you by integrating with `RequestDataValueProcessor` to ensure that forms that have an unsafe HTTP method (POST) automatically include the actual CSRF token.
 
- [[webflux-csrf-include-form-attr]]
 
- ===== CsrfToken Request Attribute
 
- If the <<webflux-csrf-include,other options>> for including the actual CSRF token in the request do not work, you can take advantage of the fact that the `Mono<CsrfToken>` <<webflux-csrf-include,is exposed>> as a `ServerWebExchange` attribute named `org.springframework.security.web.server.csrf.CsrfToken`.
 
- The following Thymeleaf sample assumes that you <<webflux-csrf-include-subscribe,expose>> the `CsrfToken` on an attribute named `_csrf`:
 
- .CSRF Token in Form with Request Attribute
 
- ====
 
- [source,html]
 
- ----
 
- <form th:action="@{/logout}"
 
- 	method="post">
 
- <input type="submit"
 
- 	value="Log out" />
 
- <input type="hidden"
 
- 	th:name="${_csrf.parameterName}"
 
- 	th:value="${_csrf.token}"/>
 
- </form>
 
- ----
 
- ====
 
- [[webflux-csrf-include-ajax]]
 
- ==== Ajax and JSON Requests
 
- If you use JSON, you cannot submit the CSRF token within an HTTP parameter.
 
- Instead, you can submit the token within a HTTP header.
 
- In the following sections, we discuss various ways of including the CSRF token as an HTTP request header in JavaScript-based applications.
 
- [[webflux-csrf-include-ajax-auto]]
 
- ===== Automatic Inclusion
 
- You can <<webflux-csrf-configure-custom-repository,configure>> Spring Security to store the expected CSRF token in a cookie.
 
- By storing the expected CSRF in a cookie, JavaScript frameworks, such as https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS], automatically include the actual CSRF token in the HTTP request headers.
 
- [[webflux-csrf-include-ajax-meta]]
 
- ===== Meta Tags
 
- An alternative pattern to <<webflux-csrf-include-form-auto,exposing the CSRF in a cookie>> is to include the CSRF token within your `meta` tags.
 
- The HTML might look something like this:
 
- .CSRF meta tag HTML
 
- ====
 
- [source,html]
 
- ----
 
- <html>
 
- <head>
 
- 	<meta name="_csrf" content="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
 
- 	<meta name="_csrf_header" content="X-CSRF-TOKEN"/>
 
- 	<!-- ... -->
 
- </head>
 
- <!-- ... -->
 
- ----
 
- ====
 
- Once the meta tags contain the CSRF token, the JavaScript code can read the meta tags and include the CSRF token as a header.
 
- If you use jQuery, you could read the meta tags with the following code:
 
- .AJAX send CSRF Token
 
- ====
 
- [source,javascript]
 
- ----
 
- $(function () {
 
- 	var token = $("meta[name='_csrf']").attr("content");
 
- 	var header = $("meta[name='_csrf_header']").attr("content");
 
- 	$(document).ajaxSend(function(e, xhr, options) {
 
- 		xhr.setRequestHeader(header, token);
 
- 	});
 
- });
 
- ----
 
- ====
 
- The following sample assumes that you <<webflux-csrf-include-subscribe,expose>> the `CsrfToken` on an attribute named `_csrf`.
 
- The following example does this with Thymeleaf:
 
- .CSRF meta tag JSP
 
- ====
 
- [source,html]
 
- ----
 
- <html>
 
- <head>
 
- 	<meta name="_csrf" th:content="${_csrf.token}"/>
 
- 	<!-- default header name is X-CSRF-TOKEN -->
 
- 	<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
 
- 	<!-- ... -->
 
- </head>
 
- <!-- ... -->
 
- ----
 
- ====
 
- [[webflux-csrf-considerations]]
 
- == CSRF Considerations
 
- There are a few special considerations to consider when implementing protection against CSRF attacks.
 
- This section discusses those considerations as it pertains to WebFlux environments.
 
- See xref:features/exploits/csrf.adoc#csrf-considerations[CSRF Considerations] for a more general discussion.
 
- [[webflux-considerations-csrf-login]]
 
- === Logging In
 
- You should xref:features/exploits/csrf.adoc#csrf-considerations-login[require CSRF for login] requests to protect against forged login attempts.
 
- Spring Security's WebFlux support automatically does this.
 
- [[webflux-considerations-csrf-logout]]
 
- === Logging Out
 
- You should xref:features/exploits/csrf.adoc#csrf-considerations-logout[require CSRF for logout] requests to protect against forging logout attempts.
 
- By default, Spring Security's `LogoutWebFilter` only processes only HTTP post requests.
 
- This ensures that logout requires a CSRF token and that a malicious user cannot forcibly log out your users.
 
- The easiest approach is to use a form to log out.
 
- If you really want a link, you can use JavaScript to have the link perform a POST (maybe on a hidden form).
 
- For browsers with JavaScript that is disabled, you can optionally have the link take the user to a logout confirmation page that performs the POST.
 
- If you really want to use HTTP GET with logout, you can do so, but remember that doing so is generally not recommended.
 
- For example, the following Java Configuration logs out when the `/logout` URL is requested with any HTTP method:
 
- // FIXME: This should be a link to log out documentation
 
- .Log out with HTTP GET
 
- ====
 
- .Java
 
- [source,java,role="primary"]
 
- ----
 
- @Bean
 
- public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
 
- 	http
 
- 		// ...
 
- 		.logout(logout -> logout.requiresLogout(new PathPatternParserServerWebExchangeMatcher("/logout")))
 
- 	return http.build();
 
- }
 
- ----
 
- .Kotlin
 
- [source,kotlin,role="secondary"]
 
- ----
 
- @Bean
 
- fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
 
-     return http {
 
-         // ...
 
-         logout {
 
-             requiresLogout = PathPatternParserServerWebExchangeMatcher("/logout")
 
-         }
 
-     }
 
- }
 
- ----
 
- ====
 
- [[webflux-considerations-csrf-timeouts]]
 
- === CSRF and Session Timeouts
 
- By default, Spring Security stores the CSRF token in the `WebSession`.
 
- This arrangement can lead to a situation where the session expires, which means that there is not an expected CSRF token to validate against.
 
- We have already discussed xref:features/exploits/csrf.adoc#csrf-considerations-login[general solutions] to session timeouts.
 
- This section discusses the specifics of CSRF timeouts as it pertains to the WebFlux support.
 
- You can change storage of the expected CSRF token to be in a cookie.
 
- For details, see the <<webflux-csrf-configure-custom-repository>> section.
 
- // FIXME: We should add a custom AccessDeniedHandler section in the reference and update the earlier links
 
- // FIXME: We need a WebFlux multipart body vs action story. WebFlux always has multipart enabled.
 
- [[webflux-csrf-considerations-multipart]]
 
- === Multipart (file upload)
 
- We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] how protecting multipart requests (file uploads) from CSRF attacks causes a https://en.wikipedia.org/wiki/Chicken_or_the_egg[chicken and the egg] problem.
 
- This section discusses how to implement placing the CSRF token in the <<webflux-csrf-considerations-multipart-body,body>> and <<webflux-csrf-considerations-multipart-url,url>> within a WebFlux application.
 
- [NOTE]
 
- ====
 
- For more information about using multipart forms with Spring, see the https://docs.spring.io/spring/docs/5.2.x/spring-framework-reference/web-reactive.html#webflux-multipart[Multipart Data] section of the Spring reference.
 
- ====
 
- [[webflux-csrf-considerations-multipart-body]]
 
- ==== Place CSRF Token in the Body
 
- We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] the trade-offs of placing the CSRF token in the body.
 
- In a WebFlux application, you can do so with the following configuration:
 
- .Enable obtaining CSRF token from multipart/form-data
 
- ====
 
- .Java
 
- [source,java,role="primary"]
 
- ----
 
- @Bean
 
- public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
 
- 	http
 
- 		// ...
 
- 		.csrf(csrf -> csrf.tokenFromMultipartDataEnabled(true))
 
- 	return http.build();
 
- }
 
- ----
 
- .Kotlin
 
- [source,kotlin,role="secondary"]
 
- ----
 
- @Bean
 
- fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
 
-     return http {
 
- 		// ...
 
-         csrf {
 
-             tokenFromMultipartDataEnabled = true
 
-         }
 
-     }
 
- }
 
- ----
 
- ====
 
- [[webflux-csrf-considerations-multipart-url]]
 
- ==== Include CSRF Token in URL
 
- We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart[already discussed] the trade-offs of placing the CSRF token in the URL.
 
- Since the `CsrfToken` is exposed as an `ServerHttpRequest` <<webflux-csrf-include,request attribute>>, we can use that to create an `action` with the CSRF token in it.
 
- An example with Thymeleaf is shown below:
 
- .CSRF Token in Action
 
- ====
 
- [source,html]
 
- ----
 
- <form method="post"
 
- 	th:action="@{/upload(${_csrf.parameterName}=${_csrf.token})}"
 
- 	enctype="multipart/form-data">
 
- ----
 
- ====
 
- [[webflux-csrf-considerations-override-method]]
 
- === HiddenHttpMethodFilter
 
- We have xref:features/exploits/csrf.adoc#csrf-considerations-override-method[already discussed] overriding the HTTP method.
 
- In a Spring WebFlux application, overriding the HTTP method is done by using https://docs.spring.io/spring-framework/docs/5.2.x/javadoc-api/org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[`HiddenHttpMethodFilter`].
 
 
  |