diff --git a/docs/manual/src/docs/asciidoc/_includes/about/exploits/csrf.adoc b/docs/manual/src/docs/asciidoc/_includes/about/exploits/csrf.adoc index bc1bbecd41..f1078f5894 100644 --- a/docs/manual/src/docs/asciidoc/_includes/about/exploits/csrf.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/about/exploits/csrf.adoc @@ -411,8 +411,3 @@ Overriding the HTTP method occurs in a filter. That filter must be placed before Spring Security's support. Note that overriding only happens on a `post`, so this is actually unlikely to cause any real problems. However, it is still best practice to ensure it is placed before Spring Security's filters. - -In Spring's Servlet support, overriding the HTTP method is done using https://docs.spring.io/spring-framework/docs/5.2.x/javadoc-api/org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[HiddenHttpMethodFilter]. -More information can be found in https://docs.spring.io/spring/docs/5.2.x/spring-framework-reference/web.html#mvc-rest-method-conversion[HTTP Method Conversion] section of the reference documentation. - -In a Spring WebFlux application, overriding the HTTP method is done using https://docs.spring.io/spring-framework/docs/5.2.x/javadoc-api/org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[HiddenHttpMethodFilter]. diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/csrf.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/csrf.adoc new file mode 100644 index 0000000000..3f4ce26546 --- /dev/null +++ b/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/csrf.adoc @@ -0,0 +1,338 @@ +[[webflux-csrf]] += Cross Site Request Forgery (CSRF) for WebFlux Environments + +This section discusses Spring Security's <> 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 +The first step to protecting against CSRF attacks is to ensure your website uses proper HTTP verbs. +This is covered in detail in <>. + +[[webflux-csrf-configure]] +=== Configure CSRF Protection +The next step is to configure Spring Security's CSRF protection within your application. +Spring Security's CSRF protection is enabled by default, but you may need to customize the configuration. +Below are a few common customizations. + +[[webflux-csrf-configure-custom-repository]] +==== Custom CsrfTokenRepository + +By default Spring Security stores the expected CSRF token in the `WebSession` using `WebSessionServerCsrfTokenRepository`. +There can be cases where users will want to configure a custom `ServerCsrfTokenRepository`. +For example, it might be desirable to persist the `CsrfToken` in a cookie to <>. + +By default the `CookieServerCsrfTokenRepository` will write to a cookie named `XSRF-TOKEN` and read it from a header named `X-XSRF-TOKEN` or the HTTP parameter `_csrf`. +These defaults come from https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS] + +You can configure `CookieCsrfTokenRepository` in Java Configuration using: + +.Store CSRF Token in a Cookie with Java Configuration +==== +[source,java] +----- +@Bean +public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http + // ... + .csrf(csrf -> csrf.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())) + return http.build(); +} +----- +==== + +[NOTE] +==== +The sample explicitly sets `cookieHttpOnly=false`. +This is necessary to allow JavaScript (i.e. AngularJS) to read it. +If you do not need the ability to read the cookie with JavaScript directly, it is recommended to omit `cookieHttpOnly=false` (by using `new CookieServerCsrfTokenRepository()` instead) to improve security. +==== + +[[webflux-csrf-configure-disable]] +==== Disable CSRF Protection +CSRF protection is enabled by default. +However, it is simple to disable CSRF protection if it <>. + +The Java configuration below will disable CSRF protection. + +.Disable CSRF Java Configuration +==== +[source,java] +---- +@Bean +public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http + // ... + .csrf(csrf -> csrf.disable())) + return http.build(); +} +---- +==== + +[[webflux-csrf-include]] +=== Include the CSRF Token + +In order for the <> to protect against CSRF attacks, we must include the actual CSRF token in the HTTP request. +This must be included in a part of the request (i.e. form parameter, HTTP header, etc) that is not automatically included in the HTTP request by the browser. + +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/csrf/CsrfToken.html[Mono] as a `ServerWebExchange` attribute named `org.springframework.security.web.server.csrf.CsrfToken`. +This means that any view technology can access the `Mono` to expose the expected token as either a <> or <>. + +[[webflux-csrf-include-subscribe]] +If your view technology does not provide a simple way to subscribe to the `Mono`, a common pattern is to use Spring's `@ControllerAdvice` to expose the `CsrfToken` directly. +For example, the following code will place the `CsrfToken` on the default attribute name (`_csrf`) used by Spring Security's <> to automatically include the CSRF token as a hidden input. + +.`CsrfToken` as `@ModelAttribute` +==== +[source,java] +---- +@ControllerAdvice +public class SecurityControllerAdvice { + @ModelAttribute + Mono csrfToken(ServerWebExchange exchange) { + Mono csrfToken = exchange.getAttribute(CsrfToken.class.getName()); + return csrfToken.doOnSuccess(token -> exchange.getAttributes() + .put(CsrfRequestDataValueProcessor.DEFAULT_CSRF_ATTR_NAME, token)); + } +} +---- +==== + +Fortunately, Thymeleaf provides <> that works without any additional work. + +[[webflux-csrf-include-form]] +==== Form URL Encoded +In order to post an HTML form the CSRF token must be included in the form as a hidden input. +For example, the rendered HTML might look like: + +.CSRF Token HTML +==== +[source,html] +---- + +---- +==== + +Next we will 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] via its https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/reactive/result/view/CsrfRequestDataValueProcessor.html[CsrfRequestDataValueProcessor]. +In order for `CsrfRequestDataValueProcessor` to work, the `Mono` must be subscribed to and the `CsrfToken` must be <> 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, Thymleaf https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#integration-with-requestdatavalueprocessor[provides support] to take care of all the boilerplate for you by integrating with `RequestDataValueProcessor` to ensure that forms that have an unsafe HTTP method (i.e. post) will automatically include the actual CSRF token. + +[[webflux-csrf-include-form-attr]] +===== CsrfToken Request Attribute + +If the <> for including the actual CSRF token in the request do not work, you can take advantage of the fact that the `Mono` <> as a `ServerWebExchange` attribute named `org.springframework.security.web.server.csrf.CsrfToken`. + +The Thymeleaf sample below assumes that you <> the `CsrfToken` on an attribute named `_csrf`. + +.CSRF Token in Form with Request Attribute +==== +[source,html] +---- +
+ + +
+---- +==== + +[[webflux-csrf-include-ajax]] +==== Ajax and JSON Requests +If you are using JSON, then it is not possible to submit the CSRF token within an HTTP parameter. +Instead you can submit the token within a HTTP header. + +In the following sections we will discuss various ways of including the CSRF token as an HTTP request header in JavaScript based applications. + +[[webflux-csrf-include-ajax-auto]] +===== Automatic Inclusion + +Spring Security can easily be <> to store the expected CSRF token in a cookie. +By storing the expected CSRF in a cookie, JavaScript frameworks like https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS] will automatically include the actual CSRF token in the HTTP request headers. + +[[webflux-csrf-include-ajax-meta]] +===== Meta tags + +An alternative pattern to <> is to include the CSRF token within your `meta` tags. +The HTML might look something like this: + +.CSRF meta tag HTML +==== +[source,html] +---- + + + + + + + +---- +==== + +Once the meta tags contained the CSRF token, the JavaScript code would read the meta tags and include the CSRF token as a header. +If you were using jQuery, this could be done with the following: + +.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 sample below assumes that you <> the `CsrfToken` on an attribute named `_csrf`. +An example of doing this with Thymeleaf is shown below: + +.CSRF meta tag JSP +==== +[source,html] +---- + + + + + + + + +---- +==== + +[[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. +Refer to <> for a more general discussion. + + +[[webflux-considerations-csrf-login]] +=== Logging In + +It is important to <> requests to protect against forging log in attempts. +Spring Security's WebFlux support does this out of the box. + +[[webflux-considerations-csrf-logout]] +=== Logging Out + +It is important to <> requests to protect against forging log out attempts. +By default Spring Security's `LogoutWebFilter` only processes HTTP post requests. +This ensures that log out 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 (i.e. maybe on a hidden form). +For browsers with JavaScript that is disabled, you can optionally have the link take the user to a log out confirmation page that will perform the POST. + +If you really want to use HTTP GET with logout you can do so, but remember this is generally not recommended. +For example, the following Java Configuration will perform logout with the URL `/logout` is requested with any HTTP method: + +// FIXME: This should be a link to log out documentation + +.Log out with HTTP GET +==== +[source,java] +---- +@Bean +public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http + // ... + .logout(logout -> logout.requiresLogout(new PathPatternParserServerWebExchangeMatcher("/logout"))) + return http.build(); +} + +---- +==== + + +[[webflux-considerations-csrf-timeouts]] +=== CSRF and Session Timeouts + +By default Spring Security stores the CSRF token in the `WebSession`. +This can lead to a situation where the session expires which means there is not an expected CSRF token to validate against. + +We've already discussed <> to session timeouts. +This section discusses the specifics of CSRF timeouts as it pertains to the WebFlux support. + +It is simple to change storage of the expected CSRF token to be in a cookie. +For details, refer to the <> section. + +// FIXME: We should add a custom AccessDeniedHandler section in the reference and update the links above + +// FIXME: We need a WebFlux multipart body vs action story. WebFlux always has multipart enabled. +[[webflux-csrf-considerations-multipart]] +=== Multipart (file upload) +We have <> 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 <> and <> within a WebFlux application. + +[NOTE] +==== +More information about using multipart forms with Spring can be found within 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 <> the trade-offs of placing the CSRF token in the body. + +In a WebFlux application, this can be configured with the following configuration: + +.Enable obtaining CSRF token from multipart/form-data +==== +[source,java] +---- +@Bean +public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { + http + // ... + .csrf(csrf -> csrf.tokenFromMultipartDataEnabled(true)) + return http.build(); +} + +---- +==== + +[[webflux-csrf-considerations-multipart-url]] +==== Include CSRF Token in URL + +We have <> the trade-offs of placing the CSRF token in the URL. +Since the `CsrfToken` is exposed as an `ServerHttpRequest` <>, 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] +---- +
+---- +==== + +[[webflux-csrf-considerations-override-method]] +=== HiddenHttpMethodFilter +We have <> overriding the HTTP method. + +In a Spring WebFlux application, overriding the HTTP method is done using https://docs.spring.io/spring-framework/docs/5.2.x/javadoc-api/org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[HiddenHttpMethodFilter]. diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/index.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/index.adoc index 55b683dfa3..396bc8ffc5 100644 --- a/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/index.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/reactive/exploits/index.adoc @@ -1,5 +1,7 @@ = Protection Against Exploits +include::csrf.adoc[leveloffset=+1] + include::headers.adoc[leveloffset=+1] include::redirect-https.adoc[leveloffset=+1] diff --git a/docs/manual/src/docs/asciidoc/_includes/reactive/webflux.adoc b/docs/manual/src/docs/asciidoc/_includes/reactive/webflux.adoc index b5f5c1f6c6..d5c501acd5 100644 --- a/docs/manual/src/docs/asciidoc/_includes/reactive/webflux.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/reactive/webflux.adoc @@ -15,6 +15,7 @@ You can find a minimal WebFlux Security configuration below: [source,java] ----- + @EnableWebFluxSecurity public class HelloWebfluxSecurityConfig { @@ -38,6 +39,7 @@ You can find an explicit version of the minimal WebFlux Security configuration b [source,java] ----- +@Configuration @EnableWebFluxSecurity public class HelloWebfluxSecurityConfig { diff --git a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf.adoc b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf.adoc index 1480c40439..4c0464df53 100644 --- a/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf.adoc +++ b/docs/manual/src/docs/asciidoc/_includes/servlet/exploits/csrf.adoc @@ -3,7 +3,7 @@ This section discusses Spring Security's <> support for servlet environments. -[[servelt-csrf-using]] +[[servlet-csrf-using]] == Using Spring Security CSRF Protection The steps to using Spring Security's CSRF protection are outlined below: