Summary
We use spring-security csrf token mechanism to generate csrf token , but occurs some error in concurrent environment , we get csrf token by this way
CsrfToken token = (CsrfToken)request.getAttribute(CsrfToken.class.getName());
String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();
CsrfTokenDTO csrfTokenDTO = new CsrfTokenDTO();
csrfTokenDTO.setSessionId(sessionId);
csrfTokenDTO.setCsrfToken(token.getToken());
If we send two requests at same time ,we will get two csrf token response from server, but the first return csrf token is invalid, in other word, one session have two csrf token, one of them is invalid. just like below
{"csrfToken":"7e5fc5e2-084e-4042-bed4-d133fe910d26","sessionId":"341AB97535817BF0D04507E65FC9CCA7","description":"Please add Header 'X-CSRF-TOKEN', and cookie '' for all Post request","sessionName":""}
{"csrfToken":"7e5fc5e2-7552e-4042-bed4-d133fe910d26","sessionId":"341AB97535817BF0D04507E65FC9CCA7","description":"Please add Header 'X-CSRF-TOKEN', and cookie '' for all Post request","sessionName":""}
we thought this behavior cause by this reason , there is no have synchronizedin CsrfFilter
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(HttpServletResponse.class.getName(), response);
// need add synchronized avoid generate two csrf token
CsrfToken csrfToken = this.tokenRepository.loadToken(request);
final boolean missingToken = csrfToken == null;
if (missingToken) {
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
request.setAttribute(CsrfToken.class.getName(), csrfToken);
request.setAttribute(csrfToken.getParameterName(), csrfToken);
if (!this.requireCsrfProtectionMatcher.matches(request)) {
filterChain.doFilter(request, response);
return;
}
}
Actual Behavior
A session corresponds to two csrf token
Expected Behavior
A session corresponds to a csrf token
Configuration
we use csrf mechanism by this way
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.addFilterBefore(multiAuthenticationFilter,UsernamePasswordAuthenticationFilter.class)
.csrf().ignoringAntMatchers(SecurityConfigConstant.IGNORE_CSRF_PATH.toArray(new String[0]))
.and()
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
Version
4.23.RELEASE
Comment From: rwinch
Thanks for the report @someoneYYYY
This won't actually help because you need to have a synchronize around the entire request response. What's more is the request/response can occur across multiple servers. This means that the lock won't necessarily work at all because the lock will be different objects. If you are running into this scenario you are better off just generating the csrf token eagerly with a single request.
Comment From: someoneYYYY
@rwinch why need synchronize around entire request response, Your meaning that every request will have its CsrfFilter , In other word, Every request will keep its CsrfFilter instance, The CsrfFilter instance is not global instance for all request . Thanks :smile:
Comment From: call-me-xiaoge
Csrf-token is not allowed to be used in concurrent environments?