1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627 |
- [[servlet-csrf]]
- = Cross Site Request Forgery (CSRF)
- :figures: servlet/exploits
- In an application where end users can xref:servlet/authentication/index.adoc[log in], it is important to consider how to protect against xref:features/exploits/csrf.adoc#csrf[Cross Site Request Forgery (CSRF)].
- Spring Security protects against CSRF attacks by default for xref:features/exploits/csrf.adoc#csrf-protection-read-only[unsafe HTTP methods], such as a POST request, so no additional code is necessary.
- You can specify the default configuration explicitly using the following:
- [[csrf-configuration]]
- .Configure CSRF Protection
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .csrf(Customizer.withDefaults());
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- csrf { }
- }
- return http.build()
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf/>
- </http>
- ----
- ======
- To learn more about CSRF protection for your application, consider the following use cases:
- * I want to <<csrf-components,understand CSRF protection's components>>
- * I need to <<migrating-to-spring-security-6,migrate an application from Spring Security 5 to 6>>
- * I want to <<csrf-token-repository-cookie,store the `CsrfToken` in a cookie>> instead of <<csrf-token-repository-httpsession,the session>>
- * I want to <<csrf-token-repository-custom,store the `CsrfToken` in a custom location>>
- * I want to <<deferred-csrf-token-opt-out,opt-out of deferred tokens>>
- * I want to <<csrf-token-request-handler-opt-out-of-breach,opt-out of BREACH protection>>
- * I need guidance integrating <<csrf-integration-form,Thymeleaf, JSPs or another view technology>> with the backend
- * I need guidance integrating <<csrf-integration-javascript,Angular or another JavaScript framework>> with the backend
- * I need guidance integrating <<csrf-integration-mobile,a mobile application or another client>> with the backend
- * I need guidance on <<csrf-access-denied-handler,handling errors>>
- * I want to <<csrf-testing,test CSRF protection>>
- * I need guidance on <<disable-csrf,disabling CSRF protection>>
- [[csrf-components]]
- == Understanding CSRF Protection's Components
- CSRF protection is provided by several components that are composed within the javadoc:org.springframework.security.web.csrf.CsrfFilter[]:
- .`CsrfFilter` Components
- image::{figures}/csrf.png[]
- CSRF protection is divided into two parts:
- 1. Make the javadoc:org.springframework.security.web.csrf.CsrfToken[] available to the application by delegating to the <<csrf-token-request-handler,`CsrfTokenRequestHandler`>>.
- 2. Determine if the request requires CSRF protection, load and validate the token, and <<csrf-access-denied-handler,handle `AccessDeniedException`>>.
- .`CsrfFilter` Processing
- image::{figures}/csrf-processing.png[]
- * image:{icondir}/number_1.png[] First, the javadoc:org.springframework.security.web.csrf.DeferredCsrfToken[] is loaded, which holds a reference to the <<csrf-token-repository,`CsrfTokenRepository`>> so that the persisted `CsrfToken` can be loaded later (in image:{icondir}/number_4.png[]).
- * image:{icondir}/number_2.png[] Second, a `Supplier<CsrfToken>` (created from `DeferredCsrfToken`) is given to the <<csrf-token-request-handler,`CsrfTokenRequestHandler`>>, which is responsible for populating a request attribute to make the `CsrfToken` available to the rest of the application.
- * image:{icondir}/number_3.png[] Next, the main CSRF protection processing begins and checks if the current request requires CSRF protection. If not required, the filter chain is continued and processing ends.
- * image:{icondir}/number_4.png[] If CSRF protection is required, the persisted `CsrfToken` is finally loaded from the `DeferredCsrfToken`.
- * image:{icondir}/number_5.png[] Continuing, the actual CSRF token provided by the client (if any) is resolved using the <<csrf-token-request-handler,`CsrfTokenRequestHandler`>>.
- * image:{icondir}/number_6.png[] The actual CSRF token is compared against the persisted `CsrfToken`. If valid, the filter chain is continued and processing ends.
- * image:{icondir}/number_7.png[] If the actual CSRF token is invalid (or missing), an `AccessDeniedException` is passed to the <<csrf-access-denied-handler,`AccessDeniedHandler`>> and processing ends.
- [[migrating-to-spring-security-6]]
- == Migrating to Spring Security 6
- When migrating from Spring Security 5 to 6, there are a few changes that may impact your application.
- The following is an overview of the aspects of CSRF protection that have changed in Spring Security 6:
- * Loading of the `CsrfToken` is now <<deferred-csrf-token,deferred by default>> to improve performance by no longer requiring the session to be loaded on every request.
- * The `CsrfToken` now includes <<csrf-token-request-handler-breach,randomness on every request by default>> to protect the CSRF token from a https://en.wikipedia.org/wiki/BREACH[BREACH] attack.
- [TIP]
- ====
- The changes in Spring Security 6 require additional configuration for single-page applications, and as such you may find the <<csrf-integration-javascript-spa>> section particularly useful.
- ====
- See the https://docs.spring.io/spring-security/reference/5.8/migration/servlet/exploits.html[Exploit Protection] section of the https://docs.spring.io/spring-security/reference/5.8/migration/index.html[Migration] chapter for more information on migrating a Spring Security 5 application.
- [[csrf-token-repository]]
- == Persisting the `CsrfToken`
- The `CsrfToken` is persisted using a `CsrfTokenRepository`.
- By default, the <<csrf-token-repository-httpsession,`HttpSessionCsrfTokenRepository`>> is used for storing tokens in a session.
- Spring Security also provides the <<csrf-token-repository-cookie,`CookieCsrfTokenRepository`>> for storing tokens in a cookie.
- You can also specify <<csrf-token-repository-custom,your own implementation>> to store tokens wherever you like.
- [[csrf-token-repository-httpsession]]
- === Using the `HttpSessionCsrfTokenRepository`
- By default, Spring Security stores the expected CSRF token in the `HttpSession` by using javadoc:org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository[], so no additional code is necessary.
- The `HttpSessionCsrfTokenRepository` reads the token from an HTTP request header named `X-CSRF-TOKEN` or the request parameter `_csrf` by default.
- You can specify the default configuration explicitly using the following configuration:
- [[csrf-token-repository-httpsession-configuration]]
- .Configure `HttpSessionCsrfTokenRepository`
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .csrf((csrf) -> csrf
- .csrfTokenRepository(new HttpSessionCsrfTokenRepository())
- );
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- csrf {
- csrfTokenRepository = HttpSessionCsrfTokenRepository()
- }
- }
- return http.build()
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf token-repository-ref="tokenRepository"/>
- </http>
- <b:bean id="tokenRepository"
- class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository"/>
- ----
- ======
- [[csrf-token-repository-cookie]]
- === Using the `CookieCsrfTokenRepository`
- You can persist the `CsrfToken` in a cookie to <<csrf-integration-javascript,support a JavaScript-based application>> using the javadoc:org.springframework.security.web.csrf.CookieCsrfTokenRepository[].
- The `CookieCsrfTokenRepository` writes to a cookie named `XSRF-TOKEN` and reads it from an HTTP request header named `X-XSRF-TOKEN` or the request parameter `_csrf` by default.
- These defaults come from Angular and its predecessor https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS].
- [TIP]
- ====
- See the https://angular.io/guide/http-security-xsrf-protection[Cross-Site Request Forgery (XSRF) protection] guide and the https://angular.io/api/common/http/HttpClientXsrfModule[HttpClientXsrfModule] for more recent information on this topic.
- ====
- You can configure the `CookieCsrfTokenRepository` using the following configuration:
- [[csrf-token-repository-cookie-configuration]]
- .Configure `CookieCsrfTokenRepository`
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .csrf((csrf) -> csrf
- .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
- );
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- csrf {
- csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse()
- }
- }
- return http.build()
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf token-repository-ref="tokenRepository"/>
- </http>
- <b:bean id="tokenRepository"
- class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
- p:cookieHttpOnly="false"/>
- ----
- ======
- [NOTE]
- ====
- The example explicitly sets `HttpOnly` to `false`.
- This is necessary to let JavaScript frameworks (such as Angular) read it.
- If you do not need the ability to read the cookie with JavaScript directly, we _recommend_ omitting `HttpOnly` (by using `new CookieCsrfTokenRepository()` instead) to improve security.
- ====
- [[csrf-token-repository-custom]]
- === Customizing the `CsrfTokenRepository`
- There can be cases where you want to implement a custom javadoc:org.springframework.security.web.csrf.CsrfTokenRepository[].
- Once you've implemented the `CsrfTokenRepository` interface, you can configure Spring Security to use it with the following configuration:
- [[csrf-token-repository-custom-configuration]]
- .Configure Custom `CsrfTokenRepository`
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .csrf((csrf) -> csrf
- .csrfTokenRepository(new CustomCsrfTokenRepository())
- );
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- csrf {
- csrfTokenRepository = CustomCsrfTokenRepository()
- }
- }
- return http.build()
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf token-repository-ref="tokenRepository"/>
- </http>
- <b:bean id="tokenRepository"
- class="example.CustomCsrfTokenRepository"/>
- ----
- ======
- [[csrf-token-request-handler]]
- == Handling the `CsrfToken`
- The `CsrfToken` is made available to an application using a `CsrfTokenRequestHandler`.
- This component is also responsible for resolving the `CsrfToken` from HTTP headers or request parameters.
- By default, the <<csrf-token-request-handler-breach,`XorCsrfTokenRequestAttributeHandler`>> is used for providing https://en.wikipedia.org/wiki/BREACH[BREACH] protection of the `CsrfToken`.
- Spring Security also provides the <<csrf-token-request-handler-plain,`CsrfTokenRequestAttributeHandler`>> for opting out of BREACH protection.
- You can also specify <<csrf-token-request-handler-custom,your own implementation>> to customize the strategy for handling and resolving tokens.
- [[csrf-token-request-handler-breach]]
- === Using the `XorCsrfTokenRequestAttributeHandler` (BREACH)
- The `XorCsrfTokenRequestAttributeHandler` makes the `CsrfToken` available as an `HttpServletRequest` attribute called `_csrf`, and additionally provides protection for https://en.wikipedia.org/wiki/BREACH[BREACH].
- [NOTE]
- ====
- The `CsrfToken` is also made available as a request attribute using the name `CsrfToken.class.getName()`.
- This name is not configurable, but the name `_csrf` can be changed using `XorCsrfTokenRequestAttributeHandler#setCsrfRequestAttributeName`.
- ====
- This implementation also resolves the token value from the request as either a request header (one of <<csrf-token-repository-httpsession,`X-CSRF-TOKEN`>> or <<csrf-token-repository-cookie,`X-XSRF-TOKEN`>> by default) or a request parameter (`_csrf` by default).
- [NOTE]
- ====
- BREACH protection is provided by encoding randomness into the CSRF token value to ensure the returned `CsrfToken` changes on every request.
- When the token is later resolved as a header value or request parameter, it is decoded to obtain the raw token which is then compared to the <<csrf-token-repository,persisted `CsrfToken`>>.
- ====
- Spring Security protects the CSRF token from a BREACH attack by default, so no additional code is necessary.
- You can specify the default configuration explicitly using the following configuration:
- [[csrf-token-request-handler-breach-configuration]]
- .Configure BREACH protection
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .csrf((csrf) -> csrf
- .csrfTokenRequestHandler(new XorCsrfTokenRequestAttributeHandler())
- );
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- csrf {
- csrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
- }
- }
- return http.build()
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf request-handler-ref="requestHandler"/>
- </http>
- <b:bean id="requestHandler"
- class="org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler"/>
- ----
- ======
- [[csrf-token-request-handler-plain]]
- === Using the `CsrfTokenRequestAttributeHandler`
- The `CsrfTokenRequestAttributeHandler` makes the `CsrfToken` available as an `HttpServletRequest` attribute called `_csrf`.
- [NOTE]
- ====
- The `CsrfToken` is also made available as a request attribute using the name `CsrfToken.class.getName()`.
- This name is not configurable, but the name `_csrf` can be changed using `CsrfTokenRequestAttributeHandler#setCsrfRequestAttributeName`.
- ====
- This implementation also resolves the token value from the request as either a request header (one of <<csrf-token-repository-httpsession,`X-CSRF-TOKEN`>> or <<csrf-token-repository-cookie,`X-XSRF-TOKEN`>> by default) or a request parameter (`_csrf` by default).
- [[csrf-token-request-handler-opt-out-of-breach]]
- The primary use of `CsrfTokenRequestAttributeHandler` is to opt-out of BREACH protection of the `CsrfToken`, which can be configured using the following configuration:
- .Opt-out of BREACH protection
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .csrf((csrf) -> csrf
- .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
- );
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- csrf {
- csrfTokenRequestHandler = CsrfTokenRequestAttributeHandler()
- }
- }
- return http.build()
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf request-handler-ref="requestHandler"/>
- </http>
- <b:bean id="requestHandler"
- class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler"/>
- ----
- ======
- [[csrf-token-request-handler-custom]]
- === Customizing the `CsrfTokenRequestHandler`
- You can implement the `CsrfTokenRequestHandler` interface to customize the strategy for handling and resolving tokens.
- [TIP]
- ====
- The `CsrfTokenRequestHandler` interface is a `@FunctionalInterface` that can be implemented using a lambda expression to customize request handling.
- You will need to implement the full interface to customize how tokens are resolved from the request.
- See <<csrf-integration-javascript-spa-configuration>> for an example that uses delegation to implement a custom strategy for handling and resolving tokens.
- ====
- Once you've implemented the `CsrfTokenRequestHandler` interface, you can configure Spring Security to use it with the following configuration:
- [[csrf-token-request-handler-custom-configuration]]
- .Configure Custom `CsrfTokenRequestHandler`
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .csrf((csrf) -> csrf
- .csrfTokenRequestHandler(new CustomCsrfTokenRequestHandler())
- );
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- csrf {
- csrfTokenRequestHandler = CustomCsrfTokenRequestHandler()
- }
- }
- return http.build()
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf request-handler-ref="requestHandler"/>
- </http>
- <b:bean id="requestHandler"
- class="example.CustomCsrfTokenRequestHandler"/>
- ----
- ======
- [[deferred-csrf-token]]
- == Deferred Loading of the `CsrfToken`
- By default, Spring Security defers loading of the `CsrfToken` until it is needed.
- [NOTE]
- ====
- The `CsrfToken` is needed whenever a request is made with an xref:features/exploits/csrf.adoc#csrf-protection-read-only[unsafe HTTP method], such as a POST.
- Additionally, it is needed by any request that renders the token to the response, such as a web page with a `<form>` tag that includes a hidden `<input>` for the CSRF token.
- ====
- Because Spring Security also stores the `CsrfToken` in the `HttpSession` by default, deferred CSRF tokens can improve performance by not requiring the session to be loaded on every request.
- [[deferred-csrf-token-opt-out]]
- In the event that you want to opt-out of deferred tokens and cause the `CsrfToken` to be loaded on every request, you can do so with the following configuration:
- [[deferred-csrf-token-opt-out-configuration]]
- .Opt-out of Deferred CSRF Tokens
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- XorCsrfTokenRequestAttributeHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();
- // set the name of the attribute the CsrfToken will be populated on
- requestHandler.setCsrfRequestAttributeName(null);
- http
- // ...
- .csrf((csrf) -> csrf
- .csrfTokenRequestHandler(requestHandler)
- );
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- val requestHandler = XorCsrfTokenRequestAttributeHandler()
- // set the name of the attribute the CsrfToken will be populated on
- requestHandler.setCsrfRequestAttributeName(null)
- http {
- // ...
- csrf {
- csrfTokenRequestHandler = requestHandler
- }
- }
- return http.build()
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf request-handler-ref="requestHandler"/>
- </http>
- <b:bean id="requestHandler"
- class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler">
- <b:property name="csrfRequestAttributeName">
- <b:null/>
- </b:property>
- </b:bean>
- ----
- ======
- [NOTE]
- ====
- By setting the `csrfRequestAttributeName` to `null`, the `CsrfToken` must first be loaded to determine what attribute name to use.
- This causes the `CsrfToken` to be loaded on every request.
- ====
- [[csrf-integration]]
- == Integrating with CSRF Protection
- 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.
- The following sections describe the various ways a frontend or client application can integrate with a CSRF-protected backend application:
- * <<csrf-integration-form>>
- * <<csrf-integration-javascript>>
- * <<csrf-integration-mobile>>
- [[csrf-integration-form]]
- === HTML Forms
- To submit 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 in HTML Form
- [source,html]
- ----
- <input type="hidden"
- name="_csrf"
- value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
- ----
- The following view technologies automatically include the actual CSRF token in a form that has an unsafe HTTP method, such as a POST:
- * 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]
- * Any other view technology that integrates with {spring-framework-api-url}org/springframework/web/servlet/support/RequestDataValueProcessor.html[`RequestDataValueProcessor`] (via javadoc:org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor[])
- * You can also include the token yourself via the xref:servlet/integrations/jsp-taglibs.adoc#taglibs-csrfinput[csrfInput] tag
- If these options are not available, you can take advantage of the fact that the `CsrfToken` is exposed as an <<csrf-token-request-handler,`HttpServletRequest` attribute named `_csrf`>>.
- The following example does this with a JSP:
- .CSRF Token in HTML 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>
- ----
- [[csrf-integration-javascript]]
- === JavaScript Applications
- JavaScript applications typically use JSON instead of HTML.
- If you use JSON, you can submit the CSRF token within an HTTP request header instead of a request parameter.
- In order to obtain the CSRF token, you can configure Spring Security to store the expected CSRF token <<csrf-token-repository-cookie,in a cookie>>.
- By storing the expected token in a cookie, JavaScript frameworks such as https://angular.io/api/common/http/HttpClientXsrfModule[Angular] can automatically include the actual CSRF token as an HTTP request header.
- [TIP]
- ====
- There are special considerations for BREACH protection and deferred tokens when integrating a single-page application (SPA) with Spring Security's CSRF protection.
- A full configuration example is provided in the <<csrf-integration-javascript-spa,next section>>.
- ====
- You can read about different types of JavaScript applications in the following sections:
- * <<csrf-integration-javascript-spa>>
- * <<csrf-integration-javascript-mpa>>
- * <<csrf-integration-javascript-other>>
- [[csrf-integration-javascript-spa]]
- ==== Single-Page Applications
- There are special considerations for integrating a single-page application (SPA) with Spring Security's CSRF protection.
- Recall that Spring Security provides <<csrf-token-request-handler-breach,BREACH protection of the `CsrfToken`>> by default.
- When storing the expected CSRF token <<csrf-token-repository-cookie,in a cookie>>, JavaScript applications will only have access to the plain token value and _will not_ have access to the encoded value.
- A <<csrf-token-request-handler-custom,customized request handler>> for resolving the actual token value will need to be provided.
- In addition, the cookie storing the CSRF token will be cleared upon authentication success and logout success.
- Spring Security defers loading a new CSRF token by default, and additional work is required to return a fresh cookie.
- [NOTE]
- ====
- Refreshing the token after authentication success and logout success is required because the javadoc:org.springframework.security.web.csrf.CsrfAuthenticationStrategy[] and javadoc:org.springframework.security.web.csrf.CsrfLogoutHandler[] will clear the previous token.
- The client application will not be able to perform an unsafe HTTP request, such as a POST, without obtaining a fresh token.
- ====
- In order to easily integrate a single-page application with Spring Security, the following configuration can be used:
- [[csrf-integration-javascript-spa-configuration]]
- .Configure CSRF for Single-Page Application
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .csrf((csrf) -> csrf
- .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // <1>
- .csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()) // <2>
- );
- return http.build();
- }
- }
- final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
- private final CsrfTokenRequestHandler plain = new CsrfTokenRequestAttributeHandler();
- private final CsrfTokenRequestHandler xor = new XorCsrfTokenRequestAttributeHandler();
- @Override
- public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
- /*
- * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
- * the CsrfToken when it is rendered in the response body.
- */
- this.xor.handle(request, response, csrfToken);
- /*
- * Render the token value to a cookie by causing the deferred token to be loaded.
- */
- csrfToken.get();
- }
- @Override
- public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
- String headerValue = request.getHeader(csrfToken.getHeaderName());
- /*
- * If the request contains a request header, use CsrfTokenRequestAttributeHandler
- * to resolve the CsrfToken. This applies when a single-page application includes
- * the header value automatically, which was obtained via a cookie containing the
- * raw CsrfToken.
- *
- * In all other cases (e.g. if the request contains a request parameter), use
- * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
- * when a server-side rendered form includes the _csrf request parameter as a
- * hidden input.
- */
- return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- csrf {
- csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse() // <1>
- csrfTokenRequestHandler = SpaCsrfTokenRequestHandler() // <2>
- }
- }
- return http.build()
- }
- }
- class SpaCsrfTokenRequestHandler : CsrfTokenRequestHandler {
- private val plain: CsrfTokenRequestHandler = CsrfTokenRequestAttributeHandler()
- private val xor: CsrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
- override fun handle(request: HttpServletRequest, response: HttpServletResponse, csrfToken: Supplier<CsrfToken>) {
- /*
- * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
- * the CsrfToken when it is rendered in the response body.
- */
- xor.handle(request, response, csrfToken)
- /*
- * Render the token value to a cookie by causing the deferred token to be loaded.
- */
- csrfToken.get()
- }
- override fun resolveCsrfTokenValue(request: HttpServletRequest, csrfToken: CsrfToken): String? {
- val headerValue = request.getHeader(csrfToken.headerName)
- /*
- * If the request contains a request header, use CsrfTokenRequestAttributeHandler
- * to resolve the CsrfToken. This applies when a single-page application includes
- * the header value automatically, which was obtained via a cookie containing the
- * raw CsrfToken.
- */
- return if (StringUtils.hasText(headerValue)) {
- plain
- } else {
- /*
- * In all other cases (e.g. if the request contains a request parameter), use
- * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
- * when a server-side rendered form includes the _csrf request parameter as a
- * hidden input.
- */
- xor
- }.resolveCsrfTokenValue(request, csrfToken)
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf
- token-repository-ref="tokenRepository" <1>
- request-handler-ref="requestHandler"/> <2>
- </http>
- <b:bean id="tokenRepository"
- class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
- p:cookieHttpOnly="false"/>
- <b:bean id="requestHandler"
- class="example.SpaCsrfTokenRequestHandler"/>
- ----
- ======
- <1> Configure `CookieCsrfTokenRepository` with `HttpOnly` set to `false` so the cookie can be read by the JavaScript application.
- <2> Configure a custom `CsrfTokenRequestHandler` that resolves the CSRF token based on whether it is an HTTP request header (`X-XSRF-TOKEN`) or request parameter (`_csrf`).
- This implementation also causes the deferred `CsrfToken` to be loaded on every request, which will return a new cookie if needed.
- [[csrf-integration-javascript-mpa]]
- ==== Multi-Page Applications
- For multi-page applications where JavaScript is loaded on each page, an alternative to exposing the CSRF token <<csrf-token-repository-cookie,in a cookie>> is to include the CSRF token within your `meta` tags.
- The HTML might look something like this:
- .CSRF Token in HTML Meta Tag
- [source,html]
- ----
- <html>
- <head>
- <meta name="_csrf" content="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
- <meta name="_csrf_header" content="X-CSRF-TOKEN"/>
- <!-- ... -->
- </head>
- <!-- ... -->
- </html>
- ----
- In order to include the CSRF token in the request, you can take advantage of the fact that the `CsrfToken` is exposed as an <<csrf-token-request-handler,`HttpServletRequest` attribute named `_csrf`>>.
- The following example does this with a JSP:
- .CSRF Token in HTML Meta Tag with Request Attribute
- [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>
- <!-- ... -->
- </html>
- ----
- 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:
- .Include CSRF Token in AJAX Request
- [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);
- });
- });
- ----
- [[csrf-integration-javascript-other]]
- ==== Other JavaScript Applications
- Another option for JavaScript applications is to include the CSRF token in an HTTP response header.
- One way to achieve this is through the use of a `@ControllerAdvice` with the xref:servlet/integrations/mvc.adoc#mvc-csrf-resolver[`CsrfTokenArgumentResolver`].
- The following is an example of `@ControllerAdvice` that applies to all controller endpoints in the application:
- [[controller-advice]]
- .CSRF Token in HTTP Response Header
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @ControllerAdvice
- public class CsrfControllerAdvice {
- @ModelAttribute
- public void getCsrfToken(HttpServletResponse response, CsrfToken csrfToken) {
- response.setHeader(csrfToken.getHeaderName(), csrfToken.getToken());
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- @ControllerAdvice
- class CsrfControllerAdvice {
- @ModelAttribute
- fun getCsrfToken(response: HttpServletResponse, csrfToken: CsrfToken) {
- response.setHeader(csrfToken.headerName, csrfToken.token)
- }
- }
- ----
- ======
- [NOTE]
- ====
- Because this `@ControllerAdvice` applies to all endpoints in the application, it will cause the CSRF token to be loaded on every request, which can negate the benefits of <<deferred-csrf-token,deferred tokens>> when using the <<csrf-token-repository-httpsession,`HttpSessionCsrfTokenRepository`>>.
- However, this is not usually an issue when using the <<csrf-token-repository-cookie,`CookieCsrfTokenRepository`>>.
- ====
- [NOTE]
- ====
- It is important to remember that controller endpoints and controller advice are called _after_ the Spring Security filter chain.
- This means that this `@ControllerAdvice` will only be applied if the request passes through the filter chain to your application.
- See the configuration for <<csrf-integration-javascript-spa-configuration,single-page applications>> for an example of adding a filter to the filter chain for earlier access to the `HttpServletResponse`.
- ====
- The CSRF token will now be available in a response header (<<csrf-token-repository-httpsession,`X-CSRF-TOKEN`>> or <<csrf-token-repository-cookie,`X-XSRF-TOKEN`>> by default) for any custom endpoints the controller advice applies to.
- Any request to the backend can be used to obtain the token from the response, and a subsequent request can include the token in a request header with the same name.
- [[csrf-integration-mobile]]
- === Mobile Applications
- Like <<csrf-integration-javascript,JavaScript applications>>, mobile applications typically use JSON instead of HTML.
- A backend application that _does not_ serve browser traffic may choose to <<disable-csrf,disable CSRF>>.
- In that case, no additional work is required.
- However, a backend application that also serves browser traffic and therefore _still requires_ CSRF protection may continue to store the `CsrfToken` <<csrf-token-repository-httpsession,in the session>> instead of <<csrf-token-repository-cookie,in a cookie>>.
- In this case, a typical pattern for integrating with the backend is to expose a `/csrf` endpoint to allow the frontend (mobile or browser client) to request a CSRF token on demand.
- The benefit of using this pattern is that the CSRF token <<deferred-csrf-token,can continue to be deferred>> and only needs to be loaded from the session when a request requires CSRF protection.
- The use of a custom endpoint also means the client application can request that a new token be generated on demand (if necessary) by issuing an explicit request.
- [TIP]
- ====
- This pattern can be used for any type of application that requires CSRF protection, not just mobile applications.
- While this approach isn't typically required in those cases, it is another option for integrating with a CSRF-protected backend.
- ====
- The following is an example of the `/csrf` endpoint that makes use of the xref:servlet/integrations/mvc.adoc#mvc-csrf-resolver[`CsrfTokenArgumentResolver`]:
- [[csrf-endpoint]]
- .The `/csrf` endpoint
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @RestController
- public class CsrfController {
- @GetMapping("/csrf")
- public CsrfToken csrf(CsrfToken csrfToken) {
- return csrfToken;
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- @RestController
- class CsrfController {
- @GetMapping("/csrf")
- fun csrf(csrfToken: CsrfToken): CsrfToken {
- return csrfToken
- }
- }
- ----
- ======
- [NOTE]
- ====
- You may consider adding `.requestMatchers("/csrf").permitAll()` if the endpoint above is required prior to authenticating with the server.
- ====
- This endpoint should be called to obtain a CSRF token when the application is launched or initialized (e.g. at load time), and also after authentication success and logout success.
- [NOTE]
- ====
- Refreshing the token after authentication success and logout success is required because the javadoc:org.springframework.security.web.csrf.CsrfAuthenticationStrategy[] and javadoc:org.springframework.security.web.csrf.CsrfLogoutHandler[] will clear the previous token.
- The client application will not be able to perform an unsafe HTTP request, such as a POST, without obtaining a fresh token.
- ====
- Once you've obtained the CSRF token, you will need to include it as an HTTP request header (one of <<csrf-token-repository-httpsession,`X-CSRF-TOKEN`>> or <<csrf-token-repository-cookie,`X-XSRF-TOKEN`>> by default) yourself.
- [[csrf-access-denied-handler]]
- == Handle `AccessDeniedException`
- To handle an `AccessDeniedException` such as `InvalidCsrfTokenException`, you can configure Spring Security to handle these exceptions in any way you like.
- For example, you can configure a custom access denied page using the following configuration:
- [[csrf-access-denied-handler-configuration]]
- .Configure `AccessDeniedHandler`
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .exceptionHandling((exceptionHandling) -> exceptionHandling
- .accessDeniedPage("/access-denied")
- );
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- exceptionHandling {
- accessDeniedPage = "/access-denied"
- }
- }
- return http.build()
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <access-denied-handler error-page="/access-denied"/>
- </http>
- ----
- ======
- [[csrf-testing]]
- == CSRF Testing
- You can use Spring Security's xref:servlet/test/mockmvc/setup.adoc[testing support] and xref:servlet/test/mockmvc/csrf.adoc[`CsrfRequestPostProcessor`] to test CSRF protection, like this:
- [[csrf-testing-example]]
- .Test CSRF Protection
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
- import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
- import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
- import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
- @ExtendWith(SpringExtension.class)
- @ContextConfiguration(classes = SecurityConfig.class)
- @WebAppConfiguration
- public class CsrfTests {
- private MockMvc mockMvc;
- @BeforeEach
- public void setUp(WebApplicationContext applicationContext) {
- this.mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext)
- .apply(springSecurity())
- .build();
- }
- @Test
- public void loginWhenValidCsrfTokenThenSuccess() throws Exception {
- this.mockMvc.perform(post("/login").with(csrf())
- .accept(MediaType.TEXT_HTML)
- .param("username", "user")
- .param("password", "password"))
- .andExpect(status().is3xxRedirection())
- .andExpect(header().string(HttpHeaders.LOCATION, "/"));
- }
- @Test
- public void loginWhenInvalidCsrfTokenThenForbidden() throws Exception {
- this.mockMvc.perform(post("/login").with(csrf().useInvalidToken())
- .accept(MediaType.TEXT_HTML)
- .param("username", "user")
- .param("password", "password"))
- .andExpect(status().isForbidden());
- }
- @Test
- public void loginWhenMissingCsrfTokenThenForbidden() throws Exception {
- this.mockMvc.perform(post("/login")
- .accept(MediaType.TEXT_HTML)
- .param("username", "user")
- .param("password", "password"))
- .andExpect(status().isForbidden());
- }
- @Test
- @WithMockUser
- public void logoutWhenValidCsrfTokenThenSuccess() throws Exception {
- this.mockMvc.perform(post("/logout").with(csrf())
- .accept(MediaType.TEXT_HTML))
- .andExpect(status().is3xxRedirection())
- .andExpect(header().string(HttpHeaders.LOCATION, "/login?logout"));
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*
- import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*
- import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
- import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
- @ExtendWith(SpringExtension::class)
- @ContextConfiguration(classes = [SecurityConfig::class])
- @WebAppConfiguration
- class CsrfTests {
- private lateinit var mockMvc: MockMvc
- @BeforeEach
- fun setUp(applicationContext: WebApplicationContext) {
- mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext)
- .apply<DefaultMockMvcBuilder>(springSecurity())
- .build()
- }
- @Test
- fun loginWhenValidCsrfTokenThenSuccess() {
- mockMvc.perform(post("/login").with(csrf())
- .accept(MediaType.TEXT_HTML)
- .param("username", "user")
- .param("password", "password"))
- .andExpect(status().is3xxRedirection)
- .andExpect(header().string(HttpHeaders.LOCATION, "/"))
- }
- @Test
- fun loginWhenInvalidCsrfTokenThenForbidden() {
- mockMvc.perform(post("/login").with(csrf().useInvalidToken())
- .accept(MediaType.TEXT_HTML)
- .param("username", "user")
- .param("password", "password"))
- .andExpect(status().isForbidden)
- }
- @Test
- fun loginWhenMissingCsrfTokenThenForbidden() {
- mockMvc.perform(post("/login")
- .accept(MediaType.TEXT_HTML)
- .param("username", "user")
- .param("password", "password"))
- .andExpect(status().isForbidden)
- }
- @Test
- @WithMockUser
- @Throws(Exception::class)
- fun logoutWhenValidCsrfTokenThenSuccess() {
- mockMvc.perform(post("/logout").with(csrf())
- .accept(MediaType.TEXT_HTML))
- .andExpect(status().is3xxRedirection)
- .andExpect(header().string(HttpHeaders.LOCATION, "/login?logout"))
- }
- }
- ----
- ======
- [[disable-csrf]]
- == Disable CSRF Protection
- By default, CSRF protection is enabled, which affects <<csrf-integration,integrating with the backend>> and <<csrf-testing,testing>> your application.
- Before disabling CSRF protection, consider whether it xref:features/exploits/csrf.adoc#csrf-when[makes sense for your application].
- You can also consider whether only certain endpoints do not require CSRF protection and configure an ignoring rule, as in the following example:
- [[disable-csrf-ignoring-configuration]]
- .Ignoring Requests
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .csrf((csrf) -> csrf
- .ignoringRequestMatchers("/api/*")
- );
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- csrf {
- ignoringRequestMatchers("/api/*")
- }
- }
- return http.build()
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf request-matcher-ref="csrfMatcher"/>
- </http>
- <b:bean id="csrfMatcher"
- class="org.springframework.security.web.util.matcher.AndRequestMatcher">
- <b:constructor-arg value="#{T(org.springframework.security.web.csrf.CsrfFilter).DEFAULT_CSRF_MATCHER}"/>
- <b:constructor-arg>
- <b:bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
- <b:bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
- <b:constructor-arg value="/api/*"/>
- </b:bean>
- </b:bean>
- </b:constructor-arg>
- </b:bean>
- ----
- ======
- If you need to disable CSRF protection, you can do so using the following configuration:
- [[disable-csrf-configuration]]
- .Disable CSRF
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .csrf((csrf) -> csrf.disable());
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- csrf {
- disable()
- }
- }
- return http.build()
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf disabled="true"/>
- </http>
- ----
- ======
- [[csrf-considerations]]
- == CSRF Considerations
- There are a few special considerations 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.
- [[csrf-considerations-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.
- [[csrf-considerations-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` will only process HTTP POST requests.
- This ensures that logging out requires a CSRF token and that a malicious user cannot forcibly log your users out.
- The easiest approach is to use a form to log the user 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 logs out when the `/logout` URL is requested with any HTTP method:
- .Log Out with Any HTTP Method
- [tabs]
- ======
- Java::
- +
- [source,java,role="primary"]
- ----
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig {
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- http
- // ...
- .logout((logout) -> logout
- .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
- );
- return http.build();
- }
- }
- ----
- Kotlin::
- +
- [source,kotlin,role="secondary"]
- ----
- import org.springframework.security.config.annotation.web.invoke
- @Configuration
- @EnableWebSecurity
- class SecurityConfig {
- @Bean
- open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
- http {
- // ...
- logout {
- logoutRequestMatcher = AntPathRequestMatcher("/logout")
- }
- }
- return http.build()
- }
- }
- ----
- ======
- See the xref:servlet/authentication/logout.adoc[Logout] chapter for more information.
- [[considerations-csrf-timeouts]]
- === CSRF and Session Timeouts
- By default, Spring Security stores the CSRF token in the `HttpSession` using the <<csrf-token-repository-httpsession,`HttpSessionCsrfTokenRepository`>>.
- 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-timeouts[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 <<csrf-token-repository-cookie>> section.
- If a token does expire, you might want to customize how it is handled by specifying a <<csrf-access-denied-handler,custom `AccessDeniedHandler`>>.
- The custom `AccessDeniedHandler` can process the `InvalidCsrfTokenException` any way you like.
- [[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.
- When JavaScript is available, we _recommend_ <<csrf-integration-javascript-other,including the CSRF token in an HTTP request header>> to side-step the issue.
- If JavaScript is not available, the following sections discuss options for placing the CSRF token in the <<csrf-considerations-multipart-body,body>> and <<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-framework/docs/current/reference/html/web.html#mvc-multipart[Multipart Resolver] section of the Spring reference and the {spring-framework-api-url}org/springframework/web/multipart/support/MultipartFilter.html[`MultipartFilter` javadoc].
- ====
- [[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.
- .Configure `MultipartFilter`
- [tabs]
- ======
- 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())
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <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>
- ----
- ======
- [NOTE]
- ====
- 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.
- ====
- [[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 <<csrf-token-request-handler,`HttpServletRequest` attribute named `_csrf`>>, 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">
- ----
- [[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 {spring-framework-api-url}org/springframework/web/filter/reactive/HiddenHttpMethodFilter.html[`HiddenHttpMethodFilter`].
- You can find more information in the https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-rest-method-conversion[HTTP Method Conversion] section of the reference documentation.
- [[csrf-further-reading]]
- == Further Reading
- Now that you have reviewed CSRF protection, consider learning more about xref:servlet/exploits/index.adoc[exploit protection] including xref:servlet/exploits/headers.adoc[secure headers] and the xref:servlet/exploits/firewall.adoc[HTTP firewall] or move on to learning how to xref:servlet/test/index.adoc[test] your application.
|