123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- [[servlet-csrf]]
- = Cross Site Request Forgery (CSRF) for Servlet Environments
- This section discusses Spring Security's xref:features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)] support for servlet environments.
- [[servlet-csrf-using]]
- == Using Spring Security CSRF Protection
- The steps to using Spring Security's CSRF protection are outlined below:
- * <<servlet-csrf-idempotent>>
- * <<servlet-csrf-configure>>
- * <<servlet-csrf-include>>
- [[servlet-csrf-idempotent]]
- === Use proper HTTP verbs
- The first step to protecting against CSRF attacks is to ensure that 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].
- [[servlet-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.
- The next few sections cover a few common customizations.
- [[servlet-csrf-configure-custom-repository]]
- ==== Custom CsrfTokenRepository
- By default, Spring Security stores the expected CSRF token in the `HttpSession` by using `HttpSessionCsrfTokenRepository`.
- There can be cases where users want to configure a custom `CsrfTokenRepository`.
- For example, it might be desirable to persist the `CsrfToken` in a cookie to <<servlet-csrf-include-ajax-auto,support a JavaScript-based application>>.
- By default, the `CookieCsrfTokenRepository` writes to a cookie named `XSRF-TOKEN` and reads 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 XML byusing the following:
- .Store CSRF Token in a Cookie with XML Configuration
- ====
- [source,xml]
- ----
- <http>
- <!-- ... -->
- <csrf token-repository-ref="tokenRepository"/>
- </http>
- <b:bean id="tokenRepository"
- class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
- p:cookieHttpOnly="false"/>
- ----
- ====
- [NOTE]
- ====
- The sample explicitly sets `cookieHttpOnly=false`.
- This is necessary to allow JavaScript (such as AngularJS) to read it.
- If you do not need the ability to read the cookie with JavaScript directly, we recommend omitting `cookieHttpOnly=false` to improve security.
- ====
- You can configure `CookieCsrfTokenRepository` in Java or Kotlin configuration by using:
- .Store CSRF Token in a Cookie
- ====
- .Java
- [source,java,role="primary"]
- ----
- @EnableWebSecurity
- public class WebSecurityConfig extends
- WebSecurityConfigurerAdapter {
- @Override
- protected void configure(HttpSecurity http) {
- http
- .csrf(csrf -> csrf
- .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
- );
- }
- }
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- @EnableWebSecurity
- class SecurityConfig : WebSecurityConfigurerAdapter() {
- override fun configure(http: HttpSecurity) {
- http {
- csrf {
- csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse()
- }
- }
- }
- }
- ----
- ====
- [NOTE]
- ====
- The sample explicitly sets `cookieHttpOnly=false`.
- This is necessary to let JavaScript (such as AngularJS) read it.
- If you do not need the ability to read the cookie with JavaScript directly, we recommend omitting `cookieHttpOnly=false` (by using `new CookieCsrfTokenRepository()` instead) to improve security.
- ====
- [[servlet-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 following XML configuration disables CSRF protection:
- .Disable CSRF XML Configuration
- ====
- [source,xml]
- ----
- <http>
- <!-- ... -->
- <csrf disabled="true"/>
- </http>
- ----
- ====
- The following Java or Kotlin configuration disables CSRF protection:
- .Disable CSRF
- ====
- .Java
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class WebSecurityConfig extends
- WebSecurityConfigurerAdapter {
- @Override
- protected void configure(HttpSecurity http) {
- http
- .csrf(csrf -> csrf.disable());
- }
- }
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- @Configuration
- @EnableWebSecurity
- class SecurityConfig : WebSecurityConfigurerAdapter() {
- override fun configure(http: HttpSecurity) {
- http {
- csrf {
- disable()
- }
- }
- }
- }
- ----
- ====
- [[servlet-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.
- This must be included in a part of the request (a form parameter, an HTTP header, or other part) 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/csrf/CsrfFilter.html[`CsrfFilter`] exposes a https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/csrf/CsrfToken.html[`CsrfToken`] as an `HttpServletRequest` attribute named `_csrf`.
- This means that any view technology can access the `CsrfToken` to expose the expected token as either a <<servlet-csrf-include-form-attr,form>> or <<servlet-csrf-include-ajax-meta-attr,meta tag>>.
- Fortunately, there are integrations listed later in this chapter that make including the token in <<servlet-csrf-include-form,form>> and <<servlet-csrf-include-ajax,ajax>> requests even easier.
- [[servlet-csrf-include-form]]
- ==== Form URL Encoded
- 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]
- ----
- <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.
- [[servlet-csrf-include-form-auto]]
- ===== Automatic CSRF Token Inclusion
- Spring Security's CSRF support provides integration with Spring's https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/support/RequestDataValueProcessor.html[`RequestDataValueProcessor`] through its https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/servlet/support/csrf/CsrfRequestDataValueProcessor.html[`CsrfRequestDataValueProcessor`].
- This means that, if you use https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-view-jsp-formtaglib[Spring’s form tag library], https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#integration-with-requestdatavalueprocessor[Thymeleaf], or any other view technology that integrates with `RequestDataValueProcessor`, then forms that have an unsafe HTTP method (such as post) automatically include the actual CSRF token.
- [[servlet-csrf-include-form-tag]]
- ===== csrfInput Tag
- If you use JSPs, you can use https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-view-jsp-formtaglib[Spring’s form tag library].
- However, if that is not an option, you can also include the token with the xref:servlet/integrations/jsp-taglibs.adoc#taglibs-csrfinput[csrfInput] tag.
- [[servlet-csrf-include-form-attr]]
- ===== CsrfToken Request Attribute
- If the <<servlet-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 `CsrfToken` <<servlet-csrf-include,is exposed>> as an `HttpServletRequest` attribute named `_csrf`.
- The following example does this with a JSP:
- .CSRF Token in Form with Request Attribute
- ====
- [source,xml]
- ----
- <c:url var="logoutUrl" value="/logout"/>
- <form action="${logoutUrl}"
- method="post">
- <input type="submit"
- value="Log out" />
- <input type="hidden"
- name="${_csrf.parameterName}"
- value="${_csrf.token}"/>
- </form>
- ----
- ====
- [[servlet-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.
- The following sections discuss various ways of including the CSRF token as an HTTP request header in JavaScript based applications.
- [[servlet-csrf-include-ajax-auto]]
- ===== Automatic Inclusion
- You can <<servlet-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.
- [[servlet-csrf-include-ajax-meta]]
- ===== Meta Tags
- An alternative pattern to <<servlet-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 can do this 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);
- });
- });
- ----
- ====
- [[servlet-csrf-include-ajax-meta-tag]]
- ====== csrfMeta tag
- If you use JSPs, one way to write the CSRF token to the `meta` tags is by using the xref:servlet/integrations/jsp-taglibs.adoc#taglibs-csrfmeta[csrfMeta] tag.
- [[servlet-csrf-include-ajax-meta-attr]]
- ====== CsrfToken Request Attribute
- If the <<servlet-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 `CsrfToken` <<servlet-csrf-include,is exposed>> as an `HttpServletRequest` attribute named `_csrf`.
- The following example does this with a JSP:
- .CSRF meta tag JSP
- ====
- [source,html]
- ----
- <html>
- <head>
- <meta name="_csrf" content="${_csrf.token}"/>
- <!-- default header name is X-CSRF-TOKEN -->
- <meta name="_csrf_header" content="${_csrf.headerName}"/>
- <!-- ... -->
- </head>
- <!-- ... -->
- ----
- ====
- [[servlet-csrf-considerations]]
- == CSRF Considerations
- There are a few special considerations to consider when implementing protection against CSRF attacks.
- This section discusses those considerations as they pertain to servlet environments.
- See xref:features/exploits/csrf.adoc#csrf-considerations[CSRF Considerations] for a more general discussion.
- [[servlet-considerations-csrf-login]]
- === Logging In
- It is important to xref:features/exploits/csrf.adoc#csrf-considerations-login[require CSRF for log in] requests to protect against forging log in attempts.
- Spring Security's servlet support does this out of the box.
- [[servlet-considerations-csrf-logout]]
- === Logging Out
- It is important to xref:features/exploits/csrf.adoc#csrf-considerations-logout[require CSRF for log out] requests to protect against forging logout attempts.
- If CSRF protection is enabled (the default), Spring Security's `LogoutFilter` to only process HTTP POST.
- This ensures that logging 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 (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 performs the POST.
- If you really want to use HTTP GET with logout, you can do so. However, remember that this is generally not recommended.
- For example, the following Java Configuration logs out when the `/logout` URL is requested with any HTTP method:
- .Log out with any HTTP method
- ====
- .Java
- [source,java,role="primary"]
- ----
- @EnableWebSecurity
- public class WebSecurityConfig extends
- WebSecurityConfigurerAdapter {
- @Override
- protected void configure(HttpSecurity http) {
- http
- .logout(logout -> logout
- .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
- );
- }
- }
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- @EnableWebSecurity
- class SecurityConfig : WebSecurityConfigurerAdapter() {
- override fun configure(http: HttpSecurity) {
- http {
- logout {
- logoutRequestMatcher = AntPathRequestMatcher("/logout")
- }
- }
- }
- }
- ----
- ====
- [[servlet-considerations-csrf-timeouts]]
- === CSRF and Session Timeouts
- By default, Spring Security stores the CSRF token in the `HttpSession`.
- This can lead to a situation where the session expires, leaving no 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 servlet support.
- You can change the storage of the CSRF token to be in a cookie.
- For details, see the <<servlet-csrf-configure-custom-repository>> section.
- If a token does expire, you might want to customize how it is handled by specifying a custom `AccessDeniedHandler`.
- The custom `AccessDeniedHandler` can process the `InvalidCsrfTokenException` any way you like.
- For an example of how to customize the `AccessDeniedHandler`, see the provided links for both xref:servlet/appendix/namespace/http.adoc#nsa-access-denied-handler[xml] and {gh-url}/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java#L64[Java configuration].
- // FIXME: We should add a custom AccessDeniedHandler section in the reference and update the links above
- [[servlet-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 <<servlet-csrf-considerations-multipart-body,body>> and <<servlet-csrf-considerations-multipart-url,url>> within a servlet application.
- [NOTE]
- ====
- You can find more information about using multipart forms with Spring in the https://docs.spring.io/spring/docs/5.2.x/spring-framework-reference/web.html#mvc-multipart[1.1.11. Multipart Resolver] section of the Spring reference and the https://docs.spring.io/spring/docs/5.2.x/javadoc-api/org/springframework/web/multipart/support/MultipartFilter.html[`MultipartFilter` javadoc].
- ====
- [[servlet-csrf-considerations-multipart-body]]
- ==== Place CSRF Token in the Body
- We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the tradeoffs of placing the CSRF token in the body.
- In this section, we discuss how to configure Spring Security to read the CSRF from the body.
- To read the CSRF token from the body, the `MultipartFilter` is specified before the Spring Security filter.
- Specifying the `MultipartFilter` before the Spring Security filter means that there is no authorization for invoking the `MultipartFilter`, which means anyone can place temporary files on your server.
- However, only authorized users can submit a file that is processed by your application.
- In general, this is the recommended approach because the temporary file upload should have a negligible impact on most servers.
- // FIXME: Document Spring Boot
- To ensure that `MultipartFilter` is specified before the Spring Security filter with XML configuration, you can ensure the `<filter-mapping>` element of the `MultipartFilter` is placed before the `springSecurityFilterChain` within the `web.xml` file:
- .Initializer MultipartFilter
- ====
- .Java
- [source,java,role="primary"]
- ----
- public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
- @Override
- protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
- insertFilters(servletContext, new MultipartFilter());
- }
- }
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- class SecurityApplicationInitializer : AbstractSecurityWebApplicationInitializer() {
- override fun beforeSpringSecurityFilterChain(servletContext: ServletContext?) {
- insertFilters(servletContext, MultipartFilter())
- }
- }
- ----
- ====
- To ensure `MultipartFilter` is specified before the Spring Security filter with XML configuration, users can ensure the <filter-mapping> element of the `MultipartFilter` is placed before the springSecurityFilterChain within the web.xml as shown below:
- .web.xml - MultipartFilter
- ====
- [source,xml]
- ----
- <filter>
- <filter-name>MultipartFilter</filter-name>
- <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
- </filter>
- <filter>
- <filter-name>springSecurityFilterChain</filter-name>
- <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>MultipartFilter</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>springSecurityFilterChain</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
- ----
- ====
- [[servlet-csrf-considerations-multipart-url]]
- ==== Include a CSRF Token in a URL
- If letting unauthorized users upload temporary files is not acceptable, an alternative is to place the `MultipartFilter` after the Spring Security filter and include the CSRF as a query parameter in the action attribute of the form.
- Since the `CsrfToken` is exposed as an `HttpServletRequest` <<servlet-csrf-include,request attribute>>, we can use that to create an `action` with the CSRF token in it.
- The following example does this with a JSP:
- .CSRF Token in Action
- ====
- [source,html]
- ----
- <form method="post"
- action="./upload?${_csrf.parameterName}=${_csrf.token}"
- enctype="multipart/form-data">
- ----
- ====
- [[servlet-csrf-considerations-override-method]]
- === HiddenHttpMethodFilter
- We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the trade-offs of placing the CSRF token in the body.
- In Spring's Servlet support, 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`].
- You can find more information in the 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.
|