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.