5

In the new Spring Boot 3.0.1 I'm adding support for CSRF protection by adding this simple configuration as per Spring documentation:

http.csrf().csrfTokenRepository(tokenRepository);

and

 @Bean
    public CsrfTokenRepository tokenRepository() {
        var tokenRepo = CookieCsrfTokenRepository.withHttpOnlyFalse();
        tokenRepo.setCookiePath("/");
        return tokenRepo;
    }

On the front-end side, there is an Angular app with import of the standard library

import {HTTP_INTERCEPTORS, HttpClientModule, HttpClientXsrfModule} from '@angular/common/http';

However I cannot make it work with the standard workflow, like

  1. Execute request to get XCSRF-TOKEN cookie from the server.
  2. Extract the token value from the cookie.
  3. Add X-XSRF-TOKEN header with the extract value from the cookie.

The front-end sends both cookie and header with the same token value and it fails in check:

public final class XorCsrfTokenRequestAttributeHandler ...

private static String getTokenValue(String actualToken, String token) {
        byte[] actualBytes;
        try {
            actualBytes = Base64.getUrlDecoder().decode(actualToken);
        }
        catch (Exception ex) {
            return null;
        }

        byte[] tokenBytes = Utf8.encode(token);
        int tokenSize = tokenBytes.length;
        if (actualBytes.length < tokenSize) {
            return null;
        }

The methods always returns null for

if (actualBytes.length < tokenSize) {
            return null;
        }

The only way to make it work is to return encoded string to be pasted in the header directly by adding endpoint, like this:

 DeferredCsrfToken deferredCsrfToken = repository.loadDeferredToken(request, response);
        requestHandler.handle(request, response, deferredCsrfToken::get);
        CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf");
        return csrfToken.getToken();

So the returned value to be used in header looks like:

ab5tfqabXomGPuLjDQk96mVMZHNOh_JnpPVM4F_hQU1sOMyWW9sLSsCtb72rWNPabyQJ01EpSUsvtctKwcQphWeAJX9ZDPmg

instead of

5adaf830-40e6-43c9-b42a-e36fd713c1a6

Any advice on what I'm missing here?

user3411289
  • 177
  • 1
  • 1
  • 6

1 Answers1

2

Several things changed with spring security 6.

The migration guide also mentioned changes to be aware of when using angular with HttpClientXsrfModule or similar on client side.

https://docs.spring.io/spring-security/reference/5.8/migration/servlet/exploits.html#_i_am_using_angularjs_or_another_javascript_framework

https://docs.spring.io/spring-security/reference/migration/servlet/exploits.html

According to spring security migration guides following configurtation should work and is the recommended way.

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    CookieCsrfTokenRepository tokenRepository = CookieCsrfTokenRepository.withHttpOnlyFalse();
    CsrfTokenRequestAttributeHandler requestHandler = new CsrfTokenRequestAttributeHandler();
    // (optional) set null to opt out of deferred tokens
    requestHandler.setCsrfRequestAttributeName(null);

    http
        // ...
        .csrf((csrf) -> csrf
            .csrfTokenRepository(tokenRepository)
            .csrfTokenRequestHandler(requestHandler)
        );

    return http.build();
}

For further details please read migration guides, so you can adapt to your needs.