123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307 |
- = Exploit Protection Migrations
- The following steps relate to changes around how to configure CSRF.
- == Defer Loading CsrfToken
- In Spring Security 5, the default behavior is that the `CsrfToken` will be loaded on every request.
- This means that in a typical setup, the `HttpSession` must be read for every request even if it is unnecessary.
- In Spring Security 6, the default is that the lookup of the `CsrfToken` will be deferred until it is needed.
- To opt into the new Spring Security 6 default, the following configuration can be used.
- [[servlet-opt-in-defer-loading-csrf-token]]
- .Defer Loading `CsrfToken`
- ====
- .Java
- [source,java,role="primary"]
- ----
- @Bean
- DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
- CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
- // set the name of the attribute the CsrfToken will be populated on
- requestHandler.setCsrfRequestAttributeName("_csrf");
- http
- // ...
- .csrf((csrf) -> csrf
- .csrfTokenRequestHandler(requestHandler)
- );
- return http.build();
- }
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- @Bean
- open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
- val requestHandler = CsrfTokenRequestAttributeHandler()
- // set the name of the attribute the CsrfToken will be populated on
- requestHandler.setCsrfRequestAttributeName("_csrf")
- 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"
- p:csrfRequestAttributeName="_csrf"/>
- ----
- ====
- If this breaks your application, then you can explicitly opt into the 5.8 defaults using the following configuration:
- .Explicit Configure `CsrfToken` with 5.8 Defaults
- ====
- .Java
- [source,java,role="primary"]
- ----
- @Bean
- DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
- CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
- // 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"]
- ----
- @Bean
- open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
- val requestHandler = CsrfTokenRequestAttributeHandler()
- // 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>
- ----
- ====
- == Protect against CSRF BREACH
- 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:
- .`CsrfToken` BREACH Protection
- ====
- .Java
- [source,java,role="primary"]
- ----
- @Bean
- DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
- XorCsrfTokenRequestAttributeHandler requestHandler = new XorCsrfTokenRequestAttributeHandler();
- // set the name of the attribute the CsrfToken will be populated on
- requestHandler.setCsrfRequestAttributeName("_csrf");
- http
- // ...
- .csrf((csrf) -> csrf
- .csrfTokenRequestHandler(requestHandler)
- );
- return http.build();
- }
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- @Bean
- open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
- val requestHandler = XorCsrfTokenRequestAttributeHandler()
- // set the name of the attribute the CsrfToken will be populated on
- requestHandler.setCsrfRequestAttributeName("_csrf")
- 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.XorCsrfTokenRequestAttributeHandler"
- p:csrfRequestAttributeName="_csrf"/>
- ----
- ====
- [[servlet-csrf-breach-opt-out]]
- === Opt-out Steps
- If configuring CSRF BREACH protection gives you trouble, take a look at these scenarios for optimal opt out behavior:
- ==== I am using AngularJS or another Javascript framework
- 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.
- 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:
- .Configure `CsrfToken` BREACH Protection to validate raw tokens
- ====
- .Java
- [source,java,role="primary"]
- ----
- @Bean
- public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
- CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
- XorCsrfTokenRequestAttributeHandler delegate = new XorCsrfTokenRequestAttributeHandler();
- // set the name of the attribute the CsrfToken will be populated on
- delegate.setCsrfRequestAttributeName("_csrf");
- // Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
- // default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
- CsrfTokenRequestHandler requestHandler = delegate::handle;
- http
- // ...
- .csrf((csrf) -> csrf
- .csrfTokenRepository(tokenRepository)
- .csrfTokenRequestHandler(requestHandler)
- );
- return http.build();
- }
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- @Bean
- open fun springSecurity(http: HttpSecurity): SecurityFilterChain {
- val tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse()
- val delegate = XorCsrfTokenRequestAttributeHandler()
- // set the name of the attribute the CsrfToken will be populated on
- delegate.setCsrfRequestAttributeName("_csrf")
- // Use only the handle() method of XorCsrfTokenRequestAttributeHandler and the
- // default implementation of resolveCsrfTokenValue() from CsrfTokenRequestHandler
- val requestHandler = CsrfTokenRequestHandler(delegate::handle)
- http {
- csrf {
- csrfTokenRepository = tokenRepository
- csrfTokenRequestHandler = requestHandler
- }
- }
- return http.build()
- }
- ----
- .XML
- [source,xml,role="secondary"]
- ----
- <http>
- <!-- ... -->
- <csrf token-repository-ref="tokenRepository"
- request-handler-ref="requestHandler"/>
- </http>
- <b:bean id="tokenRepository"
- class="org.springframework.security.web.csrf.CookieCsrfTokenRepository"
- p:cookieHttpOnly="false"/>
- ----
- ====
- ==== I need to opt out of CSRF BREACH protection for another reason
- 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.
- == CSRF BREACH with WebSocket support
- 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].
- .WebSocket Security BREACH Protection
- ====
- .Java
- [source,java,role="primary"]
- ----
- @Bean
- ChannelInterceptor csrfChannelInterceptor() {
- return new XorCsrfChannelInterceptor();
- }
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- @Bean
- open fun csrfChannelInterceptor(): ChannelInterceptor {
- return XorCsrfChannelInterceptor()
- }
- ----
- .XML
- [source,xml,role="secondary"]
- ----
- <b:bean id="csrfChannelInterceptor"
- class="org.springframework.security.messaging.web.csrf.XorCsrfChannelInterceptor"/>
- ----
- ====
- If configuring CSRF BREACH protection for WebSocket Security gives you trouble, you can configure the 5.8 default using the following configuration:
- .Configure WebSocket Security with 5.8 default
- ====
- .Java
- [source,java,role="primary"]
- ----
- @Bean
- ChannelInterceptor csrfChannelInterceptor() {
- return new CsrfChannelInterceptor();
- }
- ----
- .Kotlin
- [source,kotlin,role="secondary"]
- ----
- @Bean
- open fun csrfChannelInterceptor(): ChannelInterceptor {
- return CsrfChannelInterceptor()
- }
- ----
- .XML
- [source,xml,role="secondary"]
- ----
- <b:bean id="csrfChannelInterceptor"
- class="org.springframework.security.messaging.web.csrf.CsrfChannelInterceptor"/>
- ----
- ====
|