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?