2
0

exploits.adoc 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. = Exploit Protection Migrations
  2. The following steps relate to changes around how to configure CSRF.
  3. == Defer Loading CsrfToken
  4. In Spring Security 5, the default behavior is that the `CsrfToken` will be loaded on every request.
  5. This means that in a typical setup, the `HttpSession` must be read for every request even if it is unnecessary.
  6. In Spring Security 6, the default is that the lookup of the `CsrfToken` will be deferred until it is needed.
  7. To opt into the new Spring Security 6 default, the following configuration can be used.
  8. [[servlet-opt-in-defer-loading-csrf-token]]
  9. .Defer Loading `CsrfToken`
  10. ====
  11. .Java
  12. [source,java,role="primary"]
  13. ----
  14. @Bean
  15. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  16. CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
  17. // set the name of the attribute the CsrfToken will be populated on
  18. requestHandler.setCsrfRequestAttributeName("_csrf");
  19. http
  20. // ...
  21. .csrf((csrf) -> csrf
  22. .csrfTokenRequestHandler(requestHandler)
  23. );
  24. return http.build();
  25. }
  26. ----
  27. .Kotlin
  28. [source,kotlin,role="secondary"]
  29. ----
  30. @Bean
  31. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  32. val requestHandler = CsrfTokenRequestAttributeHandler()
  33. // set the name of the attribute the CsrfToken will be populated on
  34. requestHandler.setCsrfRequestAttributeName("_csrf")
  35. http {
  36. csrf {
  37. csrfTokenRequestHandler = requestHandler
  38. }
  39. }
  40. return http.build()
  41. }
  42. ----
  43. .XML
  44. [source,xml,role="secondary"]
  45. ----
  46. <http>
  47. <!-- ... -->
  48. <csrf request-handler-ref="requestHandler"/>
  49. </http>
  50. <b:bean id="requestHandler"
  51. class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler"
  52. p:csrfRequestAttributeName="_csrf"/>
  53. ----
  54. ====
  55. If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
  56. .Explicit Configure `CsrfToken` with 5.8 Defaults
  57. ====
  58. .Java
  59. [source,java,role="primary"]
  60. ----
  61. @Bean
  62. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  63. CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
  64. // set the name of the attribute the CsrfToken will be populated on
  65. requestHandler.setCsrfRequestAttributeName(null);
  66. http
  67. // ...
  68. .csrf((csrf) -> csrf
  69. .csrfTokenRequestHandler(requestHandler)
  70. );
  71. return http.build();
  72. }
  73. ----
  74. .Kotlin
  75. [source,kotlin,role="secondary"]
  76. ----
  77. @Bean
  78. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  79. val requestHandler = CsrfTokenRequestAttributeHandler()
  80. // set the name of the attribute the CsrfToken will be populated on
  81. requestHandler.setCsrfRequestAttributeName(null)
  82. http {
  83. csrf {
  84. csrfTokenRequestHandler = requestHandler
  85. }
  86. }
  87. return http.build()
  88. }
  89. ----
  90. .XML
  91. [source,xml,role="secondary"]
  92. ----
  93. <http>
  94. <!-- ... -->
  95. <csrf request-handler-ref="requestHandler"/>
  96. </http>
  97. <b:bean id="requestHandler"
  98. class="org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler">
  99. <b:property name="csrfRequestAttributeName">
  100. <b:null/>
  101. </b:property>
  102. </b:bean>
  103. ----
  104. ====
  105. == Protect against CSRF BREACH
  106. If the steps for <<Defer Loading CsrfToken>> work for you, then you can also opt into Spring Security 6's default support for BREACH protection of the `CsrfToken` using the following configuration:
  107. .`CsrfToken` BREACH Protection
  108. ====
  109. .Java
  110. [source,java,role="primary"]
  111. ----
  112. @Bean
  113. DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
  114. XorCsrfTokenRequestAttributeHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();
  115. // set the name of the attribute the CsrfToken will be populated on
  116. requestHandler.setCsrfRequestAttributeName("_csrf");
  117. http
  118. // ...
  119. .csrf((csrf) -> csrf
  120. .csrfTokenRequestHandler(requestHandler)
  121. );
  122. return http.build();
  123. }
  124. ----
  125. .Kotlin
  126. [source,kotlin,role="secondary"]
  127. ----
  128. @Bean
  129. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  130. val requestHandler = XorCsrfTokenRequestAttributeHandler()
  131. // set the name of the attribute the CsrfToken will be populated on
  132. requestHandler.setCsrfRequestAttributeName("_csrf")
  133. http {
  134. csrf {
  135. csrfTokenRequestHandler = requestHandler
  136. }
  137. }
  138. return http.build()
  139. }
  140. ----
  141. .XML
  142. [source,xml,role="secondary"]
  143. ----
  144. <http>
  145. <!-- ... -->
  146. <csrf request-handler-ref="requestHandler"/>
  147. </http>
  148. <b:bean id="requestHandler"
  149. class="org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler"
  150. p:csrfRequestAttributeName="_csrf"/>
  151. ----
  152. ====
  153. [[servlet-csrf-breach-opt-out]]
  154. === Opt-out Steps
  155. If configuring CSRF BREACH protection gives you trouble, take a look at these scenarios for optimal opt out behavior:
  156. ==== I am using AngularJS or another Javascript framework
  157. If you are using AngularJS and the https://angular.io/api/common/http/HttpClientXsrfModule[HttpClientXsrfModule] (or a similar module in another framework) along with `CookieCsrfTokenRepository.withHttpOnlyFalse()`, you may find that automatic support no longer works.
  158. In this case, you can configure Spring Security to validate the raw `CsrfToken` from the cookie while keeping CSRF BREACH protection of the response using a custom `CsrfTokenRequestHandler` with delegation, like so:
  159. .Configure `CsrfToken` BREACH Protection to validate raw tokens
  160. ====
  161. .Java
  162. [source,java,role="primary"]
  163. ----
  164. @Bean
  165. public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  166. CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
  167. XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
  168. // set the name of the attribute the CsrfToken will be populated on
  169. delegate.setCsrfRequestAttributeName("_csrf");
  170. // Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
  171. // default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
  172. CsrfTokenRequestHandler requestHandler = delegate::handle;
  173. http
  174. // ...
  175. .csrf((csrf) -> csrf
  176. .csrfTokenRepository(tokenRepository)
  177. .csrfTokenRequestHandler(requestHandler)
  178. );
  179. return http.build();
  180. }
  181. ----
  182. .Kotlin
  183. [source,kotlin,role="secondary"]
  184. ----
  185. @Bean
  186. open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
  187. val tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse()
  188. val delegate = XorCsrfTokenRequestAttributeHandler()
  189. // set the name of the attribute the CsrfToken will be populated on
  190. delegate.setCsrfRequestAttributeName("_csrf")
  191. // Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
  192. // default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
  193. val requestHandler = CsrfTokenRequestHandler(delegate::handle)
  194. http {
  195. csrf {
  196. csrfTokenRepository = tokenRepository
  197. csrfTokenRequestHandler = requestHandler
  198. }
  199. }
  200. return http.build()
  201. }
  202. ----
  203. .XML
  204. [source,xml,role="secondary"]
  205. ----
  206. <http>
  207. <!-- ... -->
  208. <csrf token-repository-ref="tokenRepository"
  209. request-handler-ref="requestHandler"/>
  210. </http>
  211. <b:bean id="tokenRepository"
  212. class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
  213. p:cookieHttpOnly="false"/>
  214. ----
  215. ====
  216. ==== I need to opt out of CSRF BREACH protection for another reason
  217. If CSRF BREACH protection does not work for you for another reason, you can opt out using the configuration from the <<servlet-opt-in-defer-loading-csrf-token>> section.
  218. == CSRF BREACH with WebSocket support
  219. If the steps for <<Protect against CSRF BREACH>> work for normal HTTP requests and you are using xref:servlet/integrations/websocket.adoc[WebSocket Security] support, then you can also opt into Spring Security 6's default support for BREACH protection of the `CsrfToken` with xref:servlet/integrations/websocket.adoc#websocket-sameorigin-csrf[Stomp headers].
  220. .WebSocket Security BREACH Protection
  221. ====
  222. .Java
  223. [source,java,role="primary"]
  224. ----
  225. @Bean
  226. ChannelInterceptor csrfChannelInterceptor() {
  227. return new XorCsrfChannelInterceptor();
  228. }
  229. ----
  230. .Kotlin
  231. [source,kotlin,role="secondary"]
  232. ----
  233. @Bean
  234. open fun csrfChannelInterceptor(): ChannelInterceptor {
  235. return XorCsrfChannelInterceptor()
  236. }
  237. ----
  238. .XML
  239. [source,xml,role="secondary"]
  240. ----
  241. <b:bean id="csrfChannelInterceptor"
  242. class="org.springframework.security.messaging.web.csrf.XorCsrfChannelInterceptor"/>
  243. ----
  244. ====
  245. If configuring CSRF BREACH protection for WebSocket Security gives you trouble, you can configure the 5.8 default using the following configuration:
  246. .Configure WebSocket Security with 5.8 default
  247. ====
  248. .Java
  249. [source,java,role="primary"]
  250. ----
  251. @Bean
  252. ChannelInterceptor csrfChannelInterceptor() {
  253. return new CsrfChannelInterceptor();
  254. }
  255. ----
  256. .Kotlin
  257. [source,kotlin,role="secondary"]
  258. ----
  259. @Bean
  260. open fun csrfChannelInterceptor(): ChannelInterceptor {
  261. return CsrfChannelInterceptor()
  262. }
  263. ----
  264. .XML
  265. [source,xml,role="secondary"]
  266. ----
  267. <b:bean id="csrfChannelInterceptor"
  268. class="org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor"/>
  269. ----
  270. ====