123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650 |
- [[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 {security-api-url}org/springframework/security/web/csrf/CsrfFilter.html[`CsrfFilter`]:
- .`CsrfFilter` Components
- [.invert-dark]
- image::{figures}/csrf.png[]
- CSRF protection is divided into two parts:
- 1. Make the {security-api-url}org/springframework/security/web/csrf/CsrfToken.html[`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
- [.invert-dark]
- image::{figures}/csrf-processing.png[]
- * image:{icondir}/number_1.png[] First, the {security-api-url}org/springframework/security/web/csrf/DeferredCsrfToken.html[`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 {security-api-url}org/springframework/security/web/csrf/HttpSessionCsrfTokenRepository.html[`HttpSessionCsrfTokenRepository`], so no additional code is necessary.
- The `HttpSessionCsrfTokenRepository` reads the token from a session (whether in-memory, cache, or database). If you need to access the session attribute directly, please first configure the session attribute name using `HttpSessionCsrfTokenRepository#setSessionAttributeName`.
- 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 {security-api-url}org/springframework/security/web/csrf/CookieCsrfTokenRepository.html[`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.dev/best-practices/security#httpclient-xsrf-csrf-security[HttpClient XSRF/CSRF security] and the https://angular.dev/api/common/http/withXsrfConfiguration[withXsrfConfiguration] 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 {security-api-url}org/springframework/security/web/csrf/CsrfTokenRepository.html[`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 {security-api-url}org/springframework/security/web/servlet/support/csrf/CsrfRequestDataValueProcessor.html[`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 {security-api-url}org/springframework/security/web/csrf/CsrfAuthenticationStrategy.html[`CsrfAuthenticationStrategy`] and {security-api-url}org/springframework/security/web/csrf/CsrfLogoutHandler.html[`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>
- )
- .addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class); // <3>
- return http.build();
- }
- }
- final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler {
- private final CsrfTokenRequestHandler delegate = 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.delegate.handle(request, response, csrfToken);
- }
- @Override
- public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
- /*
- * 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.
- */
- if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) {
- return super.resolveCsrfTokenValue(request, 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 this.delegate.resolveCsrfTokenValue(request, csrfToken);
- }
- }
- final class CsrfCookieFilter extends OncePerRequestFilter {
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
- // Render the token value to a cookie by causing the deferred token to be loaded
- csrfToken.getToken();
- filterChain.doFilter(request, response);
- }
- }
- ----
- 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>
- }
- }
- http.addFilterAfter(CsrfCookieFilter(), BasicAuthenticationFilter::class.java) // <3>
- return http.build()
- }
- }
- class SpaCsrfTokenRequestHandler : CsrfTokenRequestAttributeHandler() {
- private val delegate: 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.
- */
- delegate.handle(request, response, csrfToken)
- }
- override fun resolveCsrfTokenValue(request: HttpServletRequest, csrfToken: CsrfToken): String? {
- /*
- * 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(request.getHeader(csrfToken.headerName))) {
- super.resolveCsrfTokenValue(request, csrfToken)
- } 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.
- */
- delegate.resolveCsrfTokenValue(request, csrfToken)
- }
- }
- }
- class CsrfCookieFilter : OncePerRequestFilter() {
- @Throws(ServletException::class, IOException::class)
- override fun doFilterInternal(request: HttpServletRequest, response: HttpServletResponse, filterChain: FilterChain) {
- val csrfToken = request.getAttribute("_csrf") as CsrfToken
- // Render the token value to a cookie by causing the deferred token to be loaded
- csrfToken.token
- filterChain.doFilter(request, response)
- }
- }
- ----
- XML::
- +
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf
- token-repository-ref="tokenRepository" <1>
- request-handler-ref="requestHandler"/> <2>
- <custom-filter ref="csrfCookieFilter" after="BASIC_AUTH_FILTER"/> <3>
- </http>
- <b:bean id="tokenRepository"
- class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
- p:cookieHttpOnly="false"/>
- <b:bean id="requestHandler"
- class="example.SpaCsrfTokenRequestHandler"/>
- <b:bean id="csrfCookieFilter"
- class="example.CsrfCookieFilter"/>
- ----
- ======
- <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`).
- <3> Configure a custom `Filter` to load the `CsrfToken` 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 {security-api-url}org/springframework/security/web/csrf/CsrfAuthenticationStrategy.html[`CsrfAuthenticationStrategy`] and {security-api-url}org/springframework/security/web/csrf/CsrfLogoutHandler.html[`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.
|