springboot:3.1.2 springsecurity:6.12

The SecurityConfiguration code is as follows:

`@Configuration @EnableWebSecurity public class SecurityConfiguration {

@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private JwtAuthenticationFilter jwtAuthenticationFilter;

private static final String[] AUTH_WHITELIST = {
       "/captcha/*","/webjars/**","/auth/login"
        , "/doc.html", "/favicon.ico"
        , "/v3/api-docs/**", "/error",
      };


private static final String[] CSRF_URL = {
        "/captcha/verify",
};

@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.csrf((csrf)->csrf.ignoringRequestMatchers(CSRF_URL)
                    .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()));
    httpSecurity.sessionManagement((session)->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
    httpSecurity.authorizeHttpRequests(auth->auth.requestMatchers(AUTH_WHITELIST).permitAll()
                    .requestMatchers(HttpMethod.OPTIONS).permitAll()
                    .anyRequest().authenticated());
    httpSecurity.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    httpSecurity.exceptionHandling((exception)->exception.authenticationEntryPoint(jwtAuthenticationEntryPoint));
    return httpSecurity.build();
}

} `

Get the token code as follows: @GetMapping(path = "/csrf") public CsrfToken csrf(CsrfToken csrfToken) { return csrfToken; }

Browser parameters are as follows: Request URL: http://localhost:8888/api/auth/login Request Method: POST Status Code: 403 Forbidden Remote Address: 127.0.0.1:8888 Referrer Policy: strict-origin-when-cross-origin Access-Control-Allow-Origin: * cache-control: no-cache, no-store, max-age=0, must-revalidate connection: close content-type: application/json date: Mon, 31 Jul 2023 08:01:09 GMT expires: 0 pragma: no-cache set-cookie: XSRF-TOKEN=8228737f-8327-48a5-91e3-973f42e79d34; Path=/demo transfer-encoding: chunked x-content-type-options: nosniff x-frame-options: DENY x-xss-protection: 0 Accept: application/json, text/plain, */* Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Connection: keep-alive Content-Length: 37 Content-Type: application/json Host: localhost:8888 Origin: http://localhost:8888 Referer: http://localhost:8888/ sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="100", "Google Chrome";v="100" sec-ch-ua-mobile: ?0 sec-ch-ua-platform: "Windows" Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 X-XSRF-TOKEN: -hWToWNIMXd5HbNee-LiUG8lr0hIMtJoI4wqQkNbrYh72OF6zXGrw1ZwUxNUe4trGs_WaVsUgnB5AuJFRrUbeiFom79M7YdJ

XorCsrfTokenRequestAttributeHandler getTokenValue returned after the class, actualToken do not agree with token, cause the POST validation fails, the request 403

` public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) { String actualToken = super.resolveCsrfTokenValue(request, csrfToken); return getTokenValue(actualToken, csrfToken.getToken()); }

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

    byte[] tokenBytes = Utf8.encode(token);
    int tokenSize = tokenBytes.length;
    if (actualBytes.length < tokenSize) {
        return null;
    } else {
        int randomBytesSize = actualBytes.length - tokenSize;
        byte[] xoredCsrf = new byte[tokenSize];
        byte[] randomBytes = new byte[randomBytesSize];
        System.arraycopy(actualBytes, 0, randomBytes, 0, randomBytesSize);
        System.arraycopy(actualBytes, randomBytesSize, xoredCsrf, 0, tokenSize);
        byte[] csrfBytes = xorCsrf(randomBytes, xoredCsrf);
        return Utf8.decode(csrfBytes);
    }
}`

What is the reason??

Comment From: adebayoyeleye

I had a similar problem. In Spring Security 6, XorCsrfTokenRequestAttributeHandleris used by default, and to protect against BREACH attacks, it expects encoded csrf tokens in HTML form requests. That's why actualTokengets decoded before comparing with tokenBytes. You do not need this if you are passing csrf tokens in the request headers so in your case CsrfTokenRequestAttributeHandlershould be used.

If you check the Spring Documentation for SPAs here, you'll see that CSRF protection is handled differently and in a way that still protects against BREACH attacks.

Comment From: sjohnr

Thanks for getting in touch @mhiStrat, but it feels like this is a question that would be better suited to Stack Overflow. We prefer to use GitHub issues only for bugs and enhancements. Feel free to update this issue with a link to the re-posted question (so that other people can find it) or add a minimal sample that reproduces this issue if you feel this is a genuine bug.