csrf.adoc 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627
  1. [[servlet-csrf]]
  2. = Cross Site Request Forgery (CSRF)
  3. :figures: servlet/exploits
  4. 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)].
  5. 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.
  6. You can specify the default configuration explicitly using the following:
  7. [[csrf-configuration]]
  8. .Configure CSRF Protection
  9. [tabs]
  10. ======
  11. Java::
  12. +
  13. [source,java,role="primary"]
  14. ----
  15. @Configuration
  16. @EnableWebSecurity
  17. public class SecurityConfig {
  18. @Bean
  19. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  20. http
  21. // ...
  22. .csrf(Customizer.withDefaults());
  23. return http.build();
  24. }
  25. }
  26. ----
  27. Kotlin::
  28. +
  29. [source,kotlin,role="secondary"]
  30. ----
  31. import org.springframework.security.config.annotation.web.invoke
  32. @Configuration
  33. @EnableWebSecurity
  34. class SecurityConfig {
  35. @Bean
  36. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  37. http {
  38. // ...
  39. csrf { }
  40. }
  41. return http.build()
  42. }
  43. }
  44. ----
  45. XML::
  46. +
  47. [source,xml,role="secondary"]
  48. ----
  49. <http>
  50. <!-- ... -->
  51. <csrf/>
  52. </http>
  53. ----
  54. ======
  55. To learn more about CSRF protection for your application, consider the following use cases:
  56. * I want to <<csrf-components,understand CSRF protection's components>>
  57. * I need to <<migrating-to-spring-security-6,migrate an application from Spring Security 5 to 6>>
  58. * I want to <<csrf-token-repository-cookie,store the `CsrfToken` in a cookie>> instead of <<csrf-token-repository-httpsession,the session>>
  59. * I want to <<csrf-token-repository-custom,store the `CsrfToken` in a custom location>>
  60. * I want to <<deferred-csrf-token-opt-out,opt-out of deferred tokens>>
  61. * I want to <<csrf-token-request-handler-opt-out-of-breach,opt-out of BREACH protection>>
  62. * I need guidance integrating <<csrf-integration-form,Thymeleaf, JSPs or another view technology>> with the backend
  63. * I need guidance integrating <<csrf-integration-javascript,Angular or another JavaScript framework>> with the backend
  64. * I need guidance integrating <<csrf-integration-mobile,a mobile application or another client>> with the backend
  65. * I need guidance on <<csrf-access-denied-handler,handling errors>>
  66. * I want to <<csrf-testing,test CSRF protection>>
  67. * I need guidance on <<disable-csrf,disabling CSRF protection>>
  68. [[csrf-components]]
  69. == Understanding CSRF Protection's Components
  70. CSRF protection is provided by several components that are composed within the javadoc:org.springframework.security.web.csrf.CsrfFilter[]:
  71. .`CsrfFilter` Components
  72. image::{figures}/csrf.png[]
  73. CSRF protection is divided into two parts:
  74. 1. Make the javadoc:org.springframework.security.web.csrf.CsrfToken[] available to the application by delegating to the <<csrf-token-request-handler,`CsrfTokenRequestHandler`>>.
  75. 2. Determine if the request requires CSRF protection, load and validate the token, and <<csrf-access-denied-handler,handle `AccessDeniedException`>>.
  76. .`CsrfFilter` Processing
  77. image::{figures}/csrf-processing.png[]
  78. * 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[]).
  79. * 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.
  80. * 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.
  81. * image:{icondir}/number_4.png[] If CSRF protection is required, the persisted `CsrfToken` is finally loaded from the `DeferredCsrfToken`.
  82. * 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`>>.
  83. * 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.
  84. * 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.
  85. [[migrating-to-spring-security-6]]
  86. == Migrating to Spring Security 6
  87. When migrating from Spring Security 5 to 6, there are a few changes that may impact your application.
  88. The following is an overview of the aspects of CSRF protection that have changed in Spring Security 6:
  89. * 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.
  90. * 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.
  91. [TIP]
  92. ====
  93. 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.
  94. ====
  95. 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.
  96. [[csrf-token-repository]]
  97. == Persisting the `CsrfToken`
  98. The `CsrfToken` is persisted using a `CsrfTokenRepository`.
  99. By default, the <<csrf-token-repository-httpsession,`HttpSessionCsrfTokenRepository`>> is used for storing tokens in a session.
  100. Spring Security also provides the <<csrf-token-repository-cookie,`CookieCsrfTokenRepository`>> for storing tokens in a cookie.
  101. You can also specify <<csrf-token-repository-custom,your own implementation>> to store tokens wherever you like.
  102. [[csrf-token-repository-httpsession]]
  103. === Using the `HttpSessionCsrfTokenRepository`
  104. 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.
  105. The `HttpSessionCsrfTokenRepository` reads the token from an HTTP request header named `X-CSRF-TOKEN` or the request parameter `_csrf` by default.
  106. You can specify the default configuration explicitly using the following configuration:
  107. [[csrf-token-repository-httpsession-configuration]]
  108. .Configure `HttpSessionCsrfTokenRepository`
  109. [tabs]
  110. ======
  111. Java::
  112. +
  113. [source,java,role="primary"]
  114. ----
  115. @Configuration
  116. @EnableWebSecurity
  117. public class SecurityConfig {
  118. @Bean
  119. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  120. http
  121. // ...
  122. .csrf((csrf) -> csrf
  123. .csrfTokenRepository(new HttpSessionCsrfTokenRepository())
  124. );
  125. return http.build();
  126. }
  127. }
  128. ----
  129. Kotlin::
  130. +
  131. [source,kotlin,role="secondary"]
  132. ----
  133. import org.springframework.security.config.annotation.web.invoke
  134. @Configuration
  135. @EnableWebSecurity
  136. class SecurityConfig {
  137. @Bean
  138. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  139. http {
  140. // ...
  141. csrf {
  142. csrfTokenRepository = HttpSessionCsrfTokenRepository()
  143. }
  144. }
  145. return http.build()
  146. }
  147. }
  148. ----
  149. XML::
  150. +
  151. [source,xml,role="secondary"]
  152. ----
  153. <http>
  154. <!-- ... -->
  155. <csrf token-repository-ref="tokenRepository"/>
  156. </http>
  157. <b:bean id="tokenRepository"
  158. class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository"/>
  159. ----
  160. ======
  161. [[csrf-token-repository-cookie]]
  162. === Using the `CookieCsrfTokenRepository`
  163. 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[].
  164. 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.
  165. These defaults come from Angular and its predecessor https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection[AngularJS].
  166. [TIP]
  167. ====
  168. 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.
  169. ====
  170. You can configure the `CookieCsrfTokenRepository` using the following configuration:
  171. [[csrf-token-repository-cookie-configuration]]
  172. .Configure `CookieCsrfTokenRepository`
  173. [tabs]
  174. ======
  175. Java::
  176. +
  177. [source,java,role="primary"]
  178. ----
  179. @Configuration
  180. @EnableWebSecurity
  181. public class SecurityConfig {
  182. @Bean
  183. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  184. http
  185. // ...
  186. .csrf((csrf) -> csrf
  187. .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
  188. );
  189. return http.build();
  190. }
  191. }
  192. ----
  193. Kotlin::
  194. +
  195. [source,kotlin,role="secondary"]
  196. ----
  197. import org.springframework.security.config.annotation.web.invoke
  198. @Configuration
  199. @EnableWebSecurity
  200. class SecurityConfig {
  201. @Bean
  202. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  203. http {
  204. // ...
  205. csrf {
  206. csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse()
  207. }
  208. }
  209. return http.build()
  210. }
  211. }
  212. ----
  213. XML::
  214. +
  215. [source,xml,role="secondary"]
  216. ----
  217. <http>
  218. <!-- ... -->
  219. <csrf token-repository-ref="tokenRepository"/>
  220. </http>
  221. <b:bean id="tokenRepository"
  222. class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
  223. p:cookieHttpOnly="false"/>
  224. ----
  225. ======
  226. [NOTE]
  227. ====
  228. The example explicitly sets `HttpOnly` to `false`.
  229. This is necessary to let JavaScript frameworks (such as Angular) read it.
  230. 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.
  231. ====
  232. [[csrf-token-repository-custom]]
  233. === Customizing the `CsrfTokenRepository`
  234. There can be cases where you want to implement a custom javadoc:org.springframework.security.web.csrf.CsrfTokenRepository[].
  235. Once you've implemented the `CsrfTokenRepository` interface, you can configure Spring Security to use it with the following configuration:
  236. [[csrf-token-repository-custom-configuration]]
  237. .Configure Custom `CsrfTokenRepository`
  238. [tabs]
  239. ======
  240. Java::
  241. +
  242. [source,java,role="primary"]
  243. ----
  244. @Configuration
  245. @EnableWebSecurity
  246. public class SecurityConfig {
  247. @Bean
  248. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  249. http
  250. // ...
  251. .csrf((csrf) -> csrf
  252. .csrfTokenRepository(new CustomCsrfTokenRepository())
  253. );
  254. return http.build();
  255. }
  256. }
  257. ----
  258. Kotlin::
  259. +
  260. [source,kotlin,role="secondary"]
  261. ----
  262. import org.springframework.security.config.annotation.web.invoke
  263. @Configuration
  264. @EnableWebSecurity
  265. class SecurityConfig {
  266. @Bean
  267. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  268. http {
  269. // ...
  270. csrf {
  271. csrfTokenRepository = CustomCsrfTokenRepository()
  272. }
  273. }
  274. return http.build()
  275. }
  276. }
  277. ----
  278. XML::
  279. +
  280. [source,xml,role="secondary"]
  281. ----
  282. <http>
  283. <!-- ... -->
  284. <csrf token-repository-ref="tokenRepository"/>
  285. </http>
  286. <b:bean id="tokenRepository"
  287. class="example.CustomCsrfTokenRepository"/>
  288. ----
  289. ======
  290. [[csrf-token-request-handler]]
  291. == Handling the `CsrfToken`
  292. The `CsrfToken` is made available to an application using a `CsrfTokenRequestHandler`.
  293. This component is also responsible for resolving the `CsrfToken` from HTTP headers or request parameters.
  294. By default, the <<csrf-token-request-handler-breach,`XorCsrfTokenRequestAttributeHandler`>> is used for providing https://en.wikipedia.org/wiki/BREACH[BREACH] protection of the `CsrfToken`.
  295. Spring Security also provides the <<csrf-token-request-handler-plain,`CsrfTokenRequestAttributeHandler`>> for opting out of BREACH protection.
  296. You can also specify <<csrf-token-request-handler-custom,your own implementation>> to customize the strategy for handling and resolving tokens.
  297. [[csrf-token-request-handler-breach]]
  298. === Using the `XorCsrfTokenRequestAttributeHandler` (BREACH)
  299. The `XorCsrfTokenRequestAttributeHandler` makes the `CsrfToken` available as an `HttpServletRequest` attribute called `_csrf`, and additionally provides protection for https://en.wikipedia.org/wiki/BREACH[BREACH].
  300. [NOTE]
  301. ====
  302. The `CsrfToken` is also made available as a request attribute using the name `CsrfToken.class.getName()`.
  303. This name is not configurable, but the name `_csrf` can be changed using `XorCsrfTokenRequestAttributeHandler#setCsrfRequestAttributeName`.
  304. ====
  305. 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).
  306. [NOTE]
  307. ====
  308. BREACH protection is provided by encoding randomness into the CSRF token value to ensure the returned `CsrfToken` changes on every request.
  309. 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`>>.
  310. ====
  311. Spring Security protects the CSRF token from a BREACH attack by default, so no additional code is necessary.
  312. You can specify the default configuration explicitly using the following configuration:
  313. [[csrf-token-request-handler-breach-configuration]]
  314. .Configure BREACH protection
  315. [tabs]
  316. ======
  317. Java::
  318. +
  319. [source,java,role="primary"]
  320. ----
  321. @Configuration
  322. @EnableWebSecurity
  323. public class SecurityConfig {
  324. @Bean
  325. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  326. http
  327. // ...
  328. .csrf((csrf) -> csrf
  329. .csrfTokenRequestHandler(new XorCsrfTokenRequestAttributeHandler())
  330. );
  331. return http.build();
  332. }
  333. }
  334. ----
  335. Kotlin::
  336. +
  337. [source,kotlin,role="secondary"]
  338. ----
  339. import org.springframework.security.config.annotation.web.invoke
  340. @Configuration
  341. @EnableWebSecurity
  342. class SecurityConfig {
  343. @Bean
  344. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  345. http {
  346. // ...
  347. csrf {
  348. csrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
  349. }
  350. }
  351. return http.build()
  352. }
  353. }
  354. ----
  355. XML::
  356. +
  357. [source,xml,role="secondary"]
  358. ----
  359. <http>
  360. <!-- ... -->
  361. <csrf request-handler-ref="requestHandler"/>
  362. </http>
  363. <b:bean id="requestHandler"
  364. class="org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler"/>
  365. ----
  366. ======
  367. [[csrf-token-request-handler-plain]]
  368. === Using the `CsrfTokenRequestAttributeHandler`
  369. The `CsrfTokenRequestAttributeHandler` makes the `CsrfToken` available as an `HttpServletRequest` attribute called `_csrf`.
  370. [NOTE]
  371. ====
  372. The `CsrfToken` is also made available as a request attribute using the name `CsrfToken.class.getName()`.
  373. This name is not configurable, but the name `_csrf` can be changed using `CsrfTokenRequestAttributeHandler#setCsrfRequestAttributeName`.
  374. ====
  375. 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).
  376. [[csrf-token-request-handler-opt-out-of-breach]]
  377. The primary use of `CsrfTokenRequestAttributeHandler` is to opt-out of BREACH protection of the `CsrfToken`, which can be configured using the following configuration:
  378. .Opt-out of BREACH protection
  379. [tabs]
  380. ======
  381. Java::
  382. +
  383. [source,java,role="primary"]
  384. ----
  385. @Configuration
  386. @EnableWebSecurity
  387. public class SecurityConfig {
  388. @Bean
  389. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  390. http
  391. // ...
  392. .csrf((csrf) -> csrf
  393. .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
  394. );
  395. return http.build();
  396. }
  397. }
  398. ----
  399. Kotlin::
  400. +
  401. [source,kotlin,role="secondary"]
  402. ----
  403. import org.springframework.security.config.annotation.web.invoke
  404. @Configuration
  405. @EnableWebSecurity
  406. class SecurityConfig {
  407. @Bean
  408. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  409. http {
  410. // ...
  411. csrf {
  412. csrfTokenRequestHandler = CsrfTokenRequestAttributeHandler()
  413. }
  414. }
  415. return http.build()
  416. }
  417. }
  418. ----
  419. XML::
  420. +
  421. [source,xml,role="secondary"]
  422. ----
  423. <http>
  424. <!-- ... -->
  425. <csrf request-handler-ref="requestHandler"/>
  426. </http>
  427. <b:bean id="requestHandler"
  428. class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler"/>
  429. ----
  430. ======
  431. [[csrf-token-request-handler-custom]]
  432. === Customizing the `CsrfTokenRequestHandler`
  433. You can implement the `CsrfTokenRequestHandler` interface to customize the strategy for handling and resolving tokens.
  434. [TIP]
  435. ====
  436. The `CsrfTokenRequestHandler` interface is a `@FunctionalInterface` that can be implemented using a lambda expression to customize request handling.
  437. You will need to implement the full interface to customize how tokens are resolved from the request.
  438. See <<csrf-integration-javascript-spa-configuration>> for an example that uses delegation to implement a custom strategy for handling and resolving tokens.
  439. ====
  440. Once you've implemented the `CsrfTokenRequestHandler` interface, you can configure Spring Security to use it with the following configuration:
  441. [[csrf-token-request-handler-custom-configuration]]
  442. .Configure Custom `CsrfTokenRequestHandler`
  443. [tabs]
  444. ======
  445. Java::
  446. +
  447. [source,java,role="primary"]
  448. ----
  449. @Configuration
  450. @EnableWebSecurity
  451. public class SecurityConfig {
  452. @Bean
  453. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  454. http
  455. // ...
  456. .csrf((csrf) -> csrf
  457. .csrfTokenRequestHandler(new CustomCsrfTokenRequestHandler())
  458. );
  459. return http.build();
  460. }
  461. }
  462. ----
  463. Kotlin::
  464. +
  465. [source,kotlin,role="secondary"]
  466. ----
  467. import org.springframework.security.config.annotation.web.invoke
  468. @Configuration
  469. @EnableWebSecurity
  470. class SecurityConfig {
  471. @Bean
  472. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  473. http {
  474. // ...
  475. csrf {
  476. csrfTokenRequestHandler = CustomCsrfTokenRequestHandler()
  477. }
  478. }
  479. return http.build()
  480. }
  481. }
  482. ----
  483. XML::
  484. +
  485. [source,xml,role="secondary"]
  486. ----
  487. <http>
  488. <!-- ... -->
  489. <csrf request-handler-ref="requestHandler"/>
  490. </http>
  491. <b:bean id="requestHandler"
  492. class="example.CustomCsrfTokenRequestHandler"/>
  493. ----
  494. ======
  495. [[deferred-csrf-token]]
  496. == Deferred Loading of the `CsrfToken`
  497. By default, Spring Security defers loading of the `CsrfToken` until it is needed.
  498. [NOTE]
  499. ====
  500. 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.
  501. 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.
  502. ====
  503. 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.
  504. [[deferred-csrf-token-opt-out]]
  505. 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:
  506. [[deferred-csrf-token-opt-out-configuration]]
  507. .Opt-out of Deferred CSRF Tokens
  508. [tabs]
  509. ======
  510. Java::
  511. +
  512. [source,java,role="primary"]
  513. ----
  514. @Configuration
  515. @EnableWebSecurity
  516. public class SecurityConfig {
  517. @Bean
  518. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  519. XorCsrfTokenRequestAttributeHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();
  520. // set the name of the attribute the CsrfToken will be populated on
  521. requestHandler.setCsrfRequestAttributeName(null);
  522. http
  523. // ...
  524. .csrf((csrf) -> csrf
  525. .csrfTokenRequestHandler(requestHandler)
  526. );
  527. return http.build();
  528. }
  529. }
  530. ----
  531. Kotlin::
  532. +
  533. [source,kotlin,role="secondary"]
  534. ----
  535. import org.springframework.security.config.annotation.web.invoke
  536. @Configuration
  537. @EnableWebSecurity
  538. class SecurityConfig {
  539. @Bean
  540. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  541. val requestHandler = XorCsrfTokenRequestAttributeHandler()
  542. // set the name of the attribute the CsrfToken will be populated on
  543. requestHandler.setCsrfRequestAttributeName(null)
  544. http {
  545. // ...
  546. csrf {
  547. csrfTokenRequestHandler = requestHandler
  548. }
  549. }
  550. return http.build()
  551. }
  552. }
  553. ----
  554. XML::
  555. +
  556. [source,xml,role="secondary"]
  557. ----
  558. <http>
  559. <!-- ... -->
  560. <csrf request-handler-ref="requestHandler"/>
  561. </http>
  562. <b:bean id="requestHandler"
  563. class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler">
  564. <b:property name="csrfRequestAttributeName">
  565. <b:null/>
  566. </b:property>
  567. </b:bean>
  568. ----
  569. ======
  570. [NOTE]
  571. ====
  572. By setting the `csrfRequestAttributeName` to `null`, the `CsrfToken` must first be loaded to determine what attribute name to use.
  573. This causes the `CsrfToken` to be loaded on every request.
  574. ====
  575. [[csrf-integration]]
  576. == Integrating with CSRF Protection
  577. 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.
  578. 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.
  579. The following sections describe the various ways a frontend or client application can integrate with a CSRF-protected backend application:
  580. * <<csrf-integration-form>>
  581. * <<csrf-integration-javascript>>
  582. * <<csrf-integration-mobile>>
  583. [[csrf-integration-form]]
  584. === HTML Forms
  585. To submit an HTML form, the CSRF token must be included in the form as a hidden input.
  586. For example, the rendered HTML might look like:
  587. .CSRF Token in HTML Form
  588. [source,html]
  589. ----
  590. <input type="hidden"
  591. name="_csrf"
  592. value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
  593. ----
  594. The following view technologies automatically include the actual CSRF token in a form that has an unsafe HTTP method, such as a POST:
  595. * https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-view-jsp-formtaglib[Spring’s form tag library]
  596. * https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html#integration-with-requestdatavalueprocessor[Thymeleaf]
  597. * 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[])
  598. * You can also include the token yourself via the xref:servlet/integrations/jsp-taglibs.adoc#taglibs-csrfinput[csrfInput] tag
  599. 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`>>.
  600. The following example does this with a JSP:
  601. .CSRF Token in HTML Form with Request Attribute
  602. [source,xml]
  603. ----
  604. <c:url var="logoutUrl" value="/logout"/>
  605. <form action="${logoutUrl}"
  606. method="post">
  607. <input type="submit"
  608. value="Log out" />
  609. <input type="hidden"
  610. name="${_csrf.parameterName}"
  611. value="${_csrf.token}"/>
  612. </form>
  613. ----
  614. [[csrf-integration-javascript]]
  615. === JavaScript Applications
  616. JavaScript applications typically use JSON instead of HTML.
  617. If you use JSON, you can submit the CSRF token within an HTTP request header instead of a request parameter.
  618. 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>>.
  619. 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.
  620. [TIP]
  621. ====
  622. There are special considerations for BREACH protection and deferred tokens when integrating a single-page application (SPA) with Spring Security's CSRF protection.
  623. A full configuration example is provided in the <<csrf-integration-javascript-spa,next section>>.
  624. ====
  625. You can read about different types of JavaScript applications in the following sections:
  626. * <<csrf-integration-javascript-spa>>
  627. * <<csrf-integration-javascript-mpa>>
  628. * <<csrf-integration-javascript-other>>
  629. [[csrf-integration-javascript-spa]]
  630. ==== Single-Page Applications
  631. There are special considerations for integrating a single-page application (SPA) with Spring Security's CSRF protection.
  632. Recall that Spring Security provides <<csrf-token-request-handler-breach,BREACH protection of the `CsrfToken`>> by default.
  633. 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.
  634. A <<csrf-token-request-handler-custom,customized request handler>> for resolving the actual token value will need to be provided.
  635. In addition, the cookie storing the CSRF token will be cleared upon authentication success and logout success.
  636. Spring Security defers loading a new CSRF token by default, and additional work is required to return a fresh cookie.
  637. [NOTE]
  638. ====
  639. 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.
  640. The client application will not be able to perform an unsafe HTTP request, such as a POST, without obtaining a fresh token.
  641. ====
  642. In order to easily integrate a single-page application with Spring Security, the following configuration can be used:
  643. [[csrf-integration-javascript-spa-configuration]]
  644. .Configure CSRF for Single-Page Application
  645. [tabs]
  646. ======
  647. Java::
  648. +
  649. [source,java,role="primary"]
  650. ----
  651. @Configuration
  652. @EnableWebSecurity
  653. public class SecurityConfig {
  654. @Bean
  655. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  656. http
  657. // ...
  658. .csrf((csrf) -> csrf
  659. .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // <1>
  660. .csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()) // <2>
  661. );
  662. return http.build();
  663. }
  664. }
  665. final class SpaCsrfTokenRequestHandler implements CsrfTokenRequestHandler {
  666. private final CsrfTokenRequestHandler plain = new CsrfTokenRequestAttributeHandler();
  667. private final CsrfTokenRequestHandler xor = new XorCsrfTokenRequestAttributeHandler();
  668. @Override
  669. public void handle(HttpServletRequest request, HttpServletResponse response, Supplier<CsrfToken> csrfToken) {
  670. /*
  671. * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
  672. * the CsrfToken when it is rendered in the response body.
  673. */
  674. this.xor.handle(request, response, csrfToken);
  675. /*
  676. * Render the token value to a cookie by causing the deferred token to be loaded.
  677. */
  678. csrfToken.get();
  679. }
  680. @Override
  681. public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) {
  682. String headerValue = request.getHeader(csrfToken.getHeaderName());
  683. /*
  684. * If the request contains a request header, use CsrfTokenRequestAttributeHandler
  685. * to resolve the CsrfToken. This applies when a single-page application includes
  686. * the header value automatically, which was obtained via a cookie containing the
  687. * raw CsrfToken.
  688. *
  689. * In all other cases (e.g. if the request contains a request parameter), use
  690. * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
  691. * when a server-side rendered form includes the _csrf request parameter as a
  692. * hidden input.
  693. */
  694. return (StringUtils.hasText(headerValue) ? this.plain : this.xor).resolveCsrfTokenValue(request, csrfToken);
  695. }
  696. }
  697. ----
  698. Kotlin::
  699. +
  700. [source,kotlin,role="secondary"]
  701. ----
  702. import org.springframework.security.config.annotation.web.invoke
  703. @Configuration
  704. @EnableWebSecurity
  705. class SecurityConfig {
  706. @Bean
  707. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  708. http {
  709. // ...
  710. csrf {
  711. csrfTokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse() // <1>
  712. csrfTokenRequestHandler = SpaCsrfTokenRequestHandler() // <2>
  713. }
  714. }
  715. return http.build()
  716. }
  717. }
  718. class SpaCsrfTokenRequestHandler : CsrfTokenRequestHandler {
  719. private val plain: CsrfTokenRequestHandler = CsrfTokenRequestAttributeHandler()
  720. private val xor: CsrfTokenRequestHandler = XorCsrfTokenRequestAttributeHandler()
  721. override fun handle(request: HttpServletRequest, response: HttpServletResponse, csrfToken: Supplier<CsrfToken>) {
  722. /*
  723. * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of
  724. * the CsrfToken when it is rendered in the response body.
  725. */
  726. xor.handle(request, response, csrfToken)
  727. /*
  728. * Render the token value to a cookie by causing the deferred token to be loaded.
  729. */
  730. csrfToken.get()
  731. }
  732. override fun resolveCsrfTokenValue(request: HttpServletRequest, csrfToken: CsrfToken): String? {
  733. val headerValue = request.getHeader(csrfToken.headerName)
  734. /*
  735. * If the request contains a request header, use CsrfTokenRequestAttributeHandler
  736. * to resolve the CsrfToken. This applies when a single-page application includes
  737. * the header value automatically, which was obtained via a cookie containing the
  738. * raw CsrfToken.
  739. */
  740. return if (StringUtils.hasText(headerValue)) {
  741. plain
  742. } else {
  743. /*
  744. * In all other cases (e.g. if the request contains a request parameter), use
  745. * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
  746. * when a server-side rendered form includes the _csrf request parameter as a
  747. * hidden input.
  748. */
  749. xor
  750. }.resolveCsrfTokenValue(request, csrfToken)
  751. }
  752. }
  753. ----
  754. XML::
  755. +
  756. [source,xml,role="secondary"]
  757. ----
  758. <http>
  759. <!-- ... -->
  760. <csrf
  761. token-repository-ref="tokenRepository" <1>
  762. request-handler-ref="requestHandler"/> <2>
  763. </http>
  764. <b:bean id="tokenRepository"
  765. class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
  766. p:cookieHttpOnly="false"/>
  767. <b:bean id="requestHandler"
  768. class="example.SpaCsrfTokenRequestHandler"/>
  769. ----
  770. ======
  771. <1> Configure `CookieCsrfTokenRepository` with `HttpOnly` set to `false` so the cookie can be read by the JavaScript application.
  772. <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`).
  773. This implementation also causes the deferred `CsrfToken` to be loaded on every request, which will return a new cookie if needed.
  774. [[csrf-integration-javascript-mpa]]
  775. ==== Multi-Page Applications
  776. 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.
  777. The HTML might look something like this:
  778. .CSRF Token in HTML Meta Tag
  779. [source,html]
  780. ----
  781. <html>
  782. <head>
  783. <meta name="_csrf" content="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>
  784. <meta name="_csrf_header" content="X-CSRF-TOKEN"/>
  785. <!-- ... -->
  786. </head>
  787. <!-- ... -->
  788. </html>
  789. ----
  790. 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`>>.
  791. The following example does this with a JSP:
  792. .CSRF Token in HTML Meta Tag with Request Attribute
  793. [source,html]
  794. ----
  795. <html>
  796. <head>
  797. <meta name="_csrf" content="${_csrf.token}"/>
  798. <!-- default header name is X-CSRF-TOKEN -->
  799. <meta name="_csrf_header" content="${_csrf.headerName}"/>
  800. <!-- ... -->
  801. </head>
  802. <!-- ... -->
  803. </html>
  804. ----
  805. Once the meta tags contain the CSRF token, the JavaScript code can read the meta tags and include the CSRF token as a header.
  806. If you use jQuery, you can do this with the following code:
  807. .Include CSRF Token in AJAX Request
  808. [source,javascript]
  809. ----
  810. $(function () {
  811. var token = $("meta[name='_csrf']").attr("content");
  812. var header = $("meta[name='_csrf_header']").attr("content");
  813. $(document).ajaxSend(function(e, xhr, options) {
  814. xhr.setRequestHeader(header, token);
  815. });
  816. });
  817. ----
  818. [[csrf-integration-javascript-other]]
  819. ==== Other JavaScript Applications
  820. Another option for JavaScript applications is to include the CSRF token in an HTTP response header.
  821. One way to achieve this is through the use of a `@ControllerAdvice` with the xref:servlet/integrations/mvc.adoc#mvc-csrf-resolver[`CsrfTokenArgumentResolver`].
  822. The following is an example of `@ControllerAdvice` that applies to all controller endpoints in the application:
  823. [[controller-advice]]
  824. .CSRF Token in HTTP Response Header
  825. [tabs]
  826. ======
  827. Java::
  828. +
  829. [source,java,role="primary"]
  830. ----
  831. @ControllerAdvice
  832. public class CsrfControllerAdvice {
  833. @ModelAttribute
  834. public void getCsrfToken(HttpServletResponse response, CsrfToken csrfToken) {
  835. response.setHeader(csrfToken.getHeaderName(), csrfToken.getToken());
  836. }
  837. }
  838. ----
  839. Kotlin::
  840. +
  841. [source,kotlin,role="secondary"]
  842. ----
  843. @ControllerAdvice
  844. class CsrfControllerAdvice {
  845. @ModelAttribute
  846. fun getCsrfToken(response: HttpServletResponse, csrfToken: CsrfToken) {
  847. response.setHeader(csrfToken.headerName, csrfToken.token)
  848. }
  849. }
  850. ----
  851. ======
  852. [NOTE]
  853. ====
  854. 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`>>.
  855. However, this is not usually an issue when using the <<csrf-token-repository-cookie,`CookieCsrfTokenRepository`>>.
  856. ====
  857. [NOTE]
  858. ====
  859. It is important to remember that controller endpoints and controller advice are called _after_ the Spring Security filter chain.
  860. This means that this `@ControllerAdvice` will only be applied if the request passes through the filter chain to your application.
  861. 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`.
  862. ====
  863. 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.
  864. 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.
  865. [[csrf-integration-mobile]]
  866. === Mobile Applications
  867. Like <<csrf-integration-javascript,JavaScript applications>>, mobile applications typically use JSON instead of HTML.
  868. A backend application that _does not_ serve browser traffic may choose to <<disable-csrf,disable CSRF>>.
  869. In that case, no additional work is required.
  870. 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>>.
  871. 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.
  872. 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.
  873. 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.
  874. [TIP]
  875. ====
  876. This pattern can be used for any type of application that requires CSRF protection, not just mobile applications.
  877. While this approach isn't typically required in those cases, it is another option for integrating with a CSRF-protected backend.
  878. ====
  879. The following is an example of the `/csrf` endpoint that makes use of the xref:servlet/integrations/mvc.adoc#mvc-csrf-resolver[`CsrfTokenArgumentResolver`]:
  880. [[csrf-endpoint]]
  881. .The `/csrf` endpoint
  882. [tabs]
  883. ======
  884. Java::
  885. +
  886. [source,java,role="primary"]
  887. ----
  888. @RestController
  889. public class CsrfController {
  890. @GetMapping("/csrf")
  891. public CsrfToken csrf(CsrfToken csrfToken) {
  892. return csrfToken;
  893. }
  894. }
  895. ----
  896. Kotlin::
  897. +
  898. [source,kotlin,role="secondary"]
  899. ----
  900. @RestController
  901. class CsrfController {
  902. @GetMapping("/csrf")
  903. fun csrf(csrfToken: CsrfToken): CsrfToken {
  904. return csrfToken
  905. }
  906. }
  907. ----
  908. ======
  909. [NOTE]
  910. ====
  911. You may consider adding `.requestMatchers("/csrf").permitAll()` if the endpoint above is required prior to authenticating with the server.
  912. ====
  913. 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.
  914. [NOTE]
  915. ====
  916. 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.
  917. The client application will not be able to perform an unsafe HTTP request, such as a POST, without obtaining a fresh token.
  918. ====
  919. 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.
  920. [[csrf-access-denied-handler]]
  921. == Handle `AccessDeniedException`
  922. To handle an `AccessDeniedException` such as `InvalidCsrfTokenException`, you can configure Spring Security to handle these exceptions in any way you like.
  923. For example, you can configure a custom access denied page using the following configuration:
  924. [[csrf-access-denied-handler-configuration]]
  925. .Configure `AccessDeniedHandler`
  926. [tabs]
  927. ======
  928. Java::
  929. +
  930. [source,java,role="primary"]
  931. ----
  932. @Configuration
  933. @EnableWebSecurity
  934. public class SecurityConfig {
  935. @Bean
  936. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  937. http
  938. // ...
  939. .exceptionHandling((exceptionHandling) -> exceptionHandling
  940. .accessDeniedPage("/access-denied")
  941. );
  942. return http.build();
  943. }
  944. }
  945. ----
  946. Kotlin::
  947. +
  948. [source,kotlin,role="secondary"]
  949. ----
  950. import org.springframework.security.config.annotation.web.invoke
  951. @Configuration
  952. @EnableWebSecurity
  953. class SecurityConfig {
  954. @Bean
  955. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  956. http {
  957. // ...
  958. exceptionHandling {
  959. accessDeniedPage = "/access-denied"
  960. }
  961. }
  962. return http.build()
  963. }
  964. }
  965. ----
  966. XML::
  967. +
  968. [source,xml,role="secondary"]
  969. ----
  970. <http>
  971. <!-- ... -->
  972. <access-denied-handler error-page="/access-denied"/>
  973. </http>
  974. ----
  975. ======
  976. [[csrf-testing]]
  977. == CSRF Testing
  978. 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:
  979. [[csrf-testing-example]]
  980. .Test CSRF Protection
  981. [tabs]
  982. ======
  983. Java::
  984. +
  985. [source,java,role="primary"]
  986. ----
  987. import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
  988. import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
  989. import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
  990. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
  991. @ExtendWith(SpringExtension.class)
  992. @ContextConfiguration(classes = SecurityConfig.class)
  993. @WebAppConfiguration
  994. public class CsrfTests {
  995. private MockMvc mockMvc;
  996. @BeforeEach
  997. public void setUp(WebApplicationContext applicationContext) {
  998. this.mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext)
  999. .apply(springSecurity())
  1000. .build();
  1001. }
  1002. @Test
  1003. public void loginWhenValidCsrfTokenThenSuccess() throws Exception {
  1004. this.mockMvc.perform(post("/login").with(csrf())
  1005. .accept(MediaType.TEXT_HTML)
  1006. .param("username", "user")
  1007. .param("password", "password"))
  1008. .andExpect(status().is3xxRedirection())
  1009. .andExpect(header().string(HttpHeaders.LOCATION, "/"));
  1010. }
  1011. @Test
  1012. public void loginWhenInvalidCsrfTokenThenForbidden() throws Exception {
  1013. this.mockMvc.perform(post("/login").with(csrf().useInvalidToken())
  1014. .accept(MediaType.TEXT_HTML)
  1015. .param("username", "user")
  1016. .param("password", "password"))
  1017. .andExpect(status().isForbidden());
  1018. }
  1019. @Test
  1020. public void loginWhenMissingCsrfTokenThenForbidden() throws Exception {
  1021. this.mockMvc.perform(post("/login")
  1022. .accept(MediaType.TEXT_HTML)
  1023. .param("username", "user")
  1024. .param("password", "password"))
  1025. .andExpect(status().isForbidden());
  1026. }
  1027. @Test
  1028. @WithMockUser
  1029. public void logoutWhenValidCsrfTokenThenSuccess() throws Exception {
  1030. this.mockMvc.perform(post("/logout").with(csrf())
  1031. .accept(MediaType.TEXT_HTML))
  1032. .andExpect(status().is3xxRedirection())
  1033. .andExpect(header().string(HttpHeaders.LOCATION, "/login?logout"));
  1034. }
  1035. }
  1036. ----
  1037. Kotlin::
  1038. +
  1039. [source,kotlin,role="secondary"]
  1040. ----
  1041. import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*
  1042. import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*
  1043. import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*
  1044. import org.springframework.test.web.servlet.result.MockMvcResultMatchers.*
  1045. @ExtendWith(SpringExtension::class)
  1046. @ContextConfiguration(classes = [SecurityConfig::class])
  1047. @WebAppConfiguration
  1048. class CsrfTests {
  1049. private lateinit var mockMvc: MockMvc
  1050. @BeforeEach
  1051. fun setUp(applicationContext: WebApplicationContext) {
  1052. mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext)
  1053. .apply<DefaultMockMvcBuilder>(springSecurity())
  1054. .build()
  1055. }
  1056. @Test
  1057. fun loginWhenValidCsrfTokenThenSuccess() {
  1058. mockMvc.perform(post("/login").with(csrf())
  1059. .accept(MediaType.TEXT_HTML)
  1060. .param("username", "user")
  1061. .param("password", "password"))
  1062. .andExpect(status().is3xxRedirection)
  1063. .andExpect(header().string(HttpHeaders.LOCATION, "/"))
  1064. }
  1065. @Test
  1066. fun loginWhenInvalidCsrfTokenThenForbidden() {
  1067. mockMvc.perform(post("/login").with(csrf().useInvalidToken())
  1068. .accept(MediaType.TEXT_HTML)
  1069. .param("username", "user")
  1070. .param("password", "password"))
  1071. .andExpect(status().isForbidden)
  1072. }
  1073. @Test
  1074. fun loginWhenMissingCsrfTokenThenForbidden() {
  1075. mockMvc.perform(post("/login")
  1076. .accept(MediaType.TEXT_HTML)
  1077. .param("username", "user")
  1078. .param("password", "password"))
  1079. .andExpect(status().isForbidden)
  1080. }
  1081. @Test
  1082. @WithMockUser
  1083. @Throws(Exception::class)
  1084. fun logoutWhenValidCsrfTokenThenSuccess() {
  1085. mockMvc.perform(post("/logout").with(csrf())
  1086. .accept(MediaType.TEXT_HTML))
  1087. .andExpect(status().is3xxRedirection)
  1088. .andExpect(header().string(HttpHeaders.LOCATION, "/login?logout"))
  1089. }
  1090. }
  1091. ----
  1092. ======
  1093. [[disable-csrf]]
  1094. == Disable CSRF Protection
  1095. By default, CSRF protection is enabled, which affects <<csrf-integration,integrating with the backend>> and <<csrf-testing,testing>> your application.
  1096. Before disabling CSRF protection, consider whether it xref:features/exploits/csrf.adoc#csrf-when[makes sense for your application].
  1097. You can also consider whether only certain endpoints do not require CSRF protection and configure an ignoring rule, as in the following example:
  1098. [[disable-csrf-ignoring-configuration]]
  1099. .Ignoring Requests
  1100. [tabs]
  1101. ======
  1102. Java::
  1103. +
  1104. [source,java,role="primary"]
  1105. ----
  1106. @Configuration
  1107. @EnableWebSecurity
  1108. public class SecurityConfig {
  1109. @Bean
  1110. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  1111. http
  1112. // ...
  1113. .csrf((csrf) -> csrf
  1114. .ignoringRequestMatchers("/api/*")
  1115. );
  1116. return http.build();
  1117. }
  1118. }
  1119. ----
  1120. Kotlin::
  1121. +
  1122. [source,kotlin,role="secondary"]
  1123. ----
  1124. import org.springframework.security.config.annotation.web.invoke
  1125. @Configuration
  1126. @EnableWebSecurity
  1127. class SecurityConfig {
  1128. @Bean
  1129. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  1130. http {
  1131. // ...
  1132. csrf {
  1133. ignoringRequestMatchers("/api/*")
  1134. }
  1135. }
  1136. return http.build()
  1137. }
  1138. }
  1139. ----
  1140. XML::
  1141. +
  1142. [source,xml,role="secondary"]
  1143. ----
  1144. <http>
  1145. <!-- ... -->
  1146. <csrf request-matcher-ref="csrfMatcher"/>
  1147. </http>
  1148. <b:bean id="csrfMatcher"
  1149. class="org.springframework.security.web.util.matcher.AndRequestMatcher">
  1150. <b:constructor-arg value="#{T(org.springframework.security.web.csrf.CsrfFilter).DEFAULT_CSRF_MATCHER}"/>
  1151. <b:constructor-arg>
  1152. <b:bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
  1153. <b:bean class="org.springframework.security.web.util.matcher.AntPathRequestMatcher">
  1154. <b:constructor-arg value="/api/*"/>
  1155. </b:bean>
  1156. </b:bean>
  1157. </b:constructor-arg>
  1158. </b:bean>
  1159. ----
  1160. ======
  1161. If you need to disable CSRF protection, you can do so using the following configuration:
  1162. [[disable-csrf-configuration]]
  1163. .Disable CSRF
  1164. [tabs]
  1165. ======
  1166. Java::
  1167. +
  1168. [source,java,role="primary"]
  1169. ----
  1170. @Configuration
  1171. @EnableWebSecurity
  1172. public class SecurityConfig {
  1173. @Bean
  1174. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  1175. http
  1176. // ...
  1177. .csrf((csrf) -> csrf.disable());
  1178. return http.build();
  1179. }
  1180. }
  1181. ----
  1182. Kotlin::
  1183. +
  1184. [source,kotlin,role="secondary"]
  1185. ----
  1186. import org.springframework.security.config.annotation.web.invoke
  1187. @Configuration
  1188. @EnableWebSecurity
  1189. class SecurityConfig {
  1190. @Bean
  1191. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  1192. http {
  1193. // ...
  1194. csrf {
  1195. disable()
  1196. }
  1197. }
  1198. return http.build()
  1199. }
  1200. }
  1201. ----
  1202. XML::
  1203. +
  1204. [source,xml,role="secondary"]
  1205. ----
  1206. <http>
  1207. <!-- ... -->
  1208. <csrf disabled="true"/>
  1209. </http>
  1210. ----
  1211. ======
  1212. [[csrf-considerations]]
  1213. == CSRF Considerations
  1214. There are a few special considerations when implementing protection against CSRF attacks.
  1215. This section discusses those considerations as they pertain to servlet environments.
  1216. See xref:features/exploits/csrf.adoc#csrf-considerations[CSRF Considerations] for a more general discussion.
  1217. [[csrf-considerations-login]]
  1218. === Logging In
  1219. 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.
  1220. Spring Security's servlet support does this out of the box.
  1221. [[csrf-considerations-logout]]
  1222. === Logging Out
  1223. It is important to xref:features/exploits/csrf.adoc#csrf-considerations-logout[require CSRF for log out] requests to protect against forging logout attempts.
  1224. If CSRF protection is enabled (the default), Spring Security's `LogoutFilter` will only process HTTP POST requests.
  1225. This ensures that logging out requires a CSRF token and that a malicious user cannot forcibly log your users out.
  1226. The easiest approach is to use a form to log the user out.
  1227. If you really want a link, you can use JavaScript to have the link perform a POST (maybe on a hidden form).
  1228. 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.
  1229. If you really want to use HTTP GET with logout, you can do so.
  1230. However, remember that this is generally not recommended.
  1231. For example, the following logs out when the `/logout` URL is requested with any HTTP method:
  1232. .Log Out with Any HTTP Method
  1233. [tabs]
  1234. ======
  1235. Java::
  1236. +
  1237. [source,java,role="primary"]
  1238. ----
  1239. @Configuration
  1240. @EnableWebSecurity
  1241. public class SecurityConfig {
  1242. @Bean
  1243. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  1244. http
  1245. // ...
  1246. .logout((logout) -> logout
  1247. .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
  1248. );
  1249. return http.build();
  1250. }
  1251. }
  1252. ----
  1253. Kotlin::
  1254. +
  1255. [source,kotlin,role="secondary"]
  1256. ----
  1257. import org.springframework.security.config.annotation.web.invoke
  1258. @Configuration
  1259. @EnableWebSecurity
  1260. class SecurityConfig {
  1261. @Bean
  1262. open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
  1263. http {
  1264. // ...
  1265. logout {
  1266. logoutRequestMatcher = AntPathRequestMatcher("/logout")
  1267. }
  1268. }
  1269. return http.build()
  1270. }
  1271. }
  1272. ----
  1273. ======
  1274. See the xref:servlet/authentication/logout.adoc[Logout] chapter for more information.
  1275. [[considerations-csrf-timeouts]]
  1276. === CSRF and Session Timeouts
  1277. By default, Spring Security stores the CSRF token in the `HttpSession` using the <<csrf-token-repository-httpsession,`HttpSessionCsrfTokenRepository`>>.
  1278. This can lead to a situation where the session expires, leaving no CSRF token to validate against.
  1279. We have already discussed xref:features/exploits/csrf.adoc#csrf-considerations-timeouts[general solutions] to session timeouts.
  1280. This section discusses the specifics of CSRF timeouts as it pertains to the servlet support.
  1281. You can change the storage of the CSRF token to be in a cookie.
  1282. For details, see the <<csrf-token-repository-cookie>> section.
  1283. If a token does expire, you might want to customize how it is handled by specifying a <<csrf-access-denied-handler,custom `AccessDeniedHandler`>>.
  1284. The custom `AccessDeniedHandler` can process the `InvalidCsrfTokenException` any way you like.
  1285. [[csrf-considerations-multipart]]
  1286. === Multipart (file upload)
  1287. 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.
  1288. When JavaScript is available, we _recommend_ <<csrf-integration-javascript-other,including the CSRF token in an HTTP request header>> to side-step the issue.
  1289. 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.
  1290. [NOTE]
  1291. ====
  1292. 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].
  1293. ====
  1294. [[csrf-considerations-multipart-body]]
  1295. ==== Place CSRF Token in the Body
  1296. We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the tradeoffs of placing the CSRF token in the body.
  1297. In this section, we discuss how to configure Spring Security to read the CSRF from the body.
  1298. To read the CSRF token from the body, the `MultipartFilter` is specified before the Spring Security filter.
  1299. 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.
  1300. However, only authorized users can submit a file that is processed by your application.
  1301. In general, this is the recommended approach because the temporary file upload should have a negligible impact on most servers.
  1302. .Configure `MultipartFilter`
  1303. [tabs]
  1304. ======
  1305. Java::
  1306. +
  1307. [source,java,role="primary"]
  1308. ----
  1309. public class SecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
  1310. @Override
  1311. protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
  1312. insertFilters(servletContext, new MultipartFilter());
  1313. }
  1314. }
  1315. ----
  1316. Kotlin::
  1317. +
  1318. [source,kotlin,role="secondary"]
  1319. ----
  1320. class SecurityApplicationInitializer : AbstractSecurityWebApplicationInitializer() {
  1321. override fun beforeSpringSecurityFilterChain(servletContext: ServletContext?) {
  1322. insertFilters(servletContext, MultipartFilter())
  1323. }
  1324. }
  1325. ----
  1326. XML::
  1327. +
  1328. [source,xml,role="secondary"]
  1329. ----
  1330. <filter>
  1331. <filter-name>MultipartFilter</filter-name>
  1332. <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
  1333. </filter>
  1334. <filter>
  1335. <filter-name>springSecurityFilterChain</filter-name>
  1336. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  1337. </filter>
  1338. <filter-mapping>
  1339. <filter-name>MultipartFilter</filter-name>
  1340. <url-pattern>/*</url-pattern>
  1341. </filter-mapping>
  1342. <filter-mapping>
  1343. <filter-name>springSecurityFilterChain</filter-name>
  1344. <url-pattern>/*</url-pattern>
  1345. </filter-mapping>
  1346. ----
  1347. ======
  1348. [NOTE]
  1349. ====
  1350. 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.
  1351. ====
  1352. [[csrf-considerations-multipart-url]]
  1353. ==== Include a CSRF Token in a URL
  1354. 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.
  1355. 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.
  1356. The following example does this with a JSP:
  1357. .CSRF Token in Action
  1358. [source,html]
  1359. ----
  1360. <form method="post"
  1361. action="./upload?${_csrf.parameterName}=${_csrf.token}"
  1362. enctype="multipart/form-data">
  1363. ----
  1364. [[csrf-considerations-override-method]]
  1365. === HiddenHttpMethodFilter
  1366. We have xref:features/exploits/csrf.adoc#csrf-considerations-multipart-body[already discussed] the trade-offs of placing the CSRF token in the body.
  1367. 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`].
  1368. 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.
  1369. [[csrf-further-reading]]
  1370. == Further Reading
  1371. 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.