springboot:3.2.1 springsecurity:6.2.1

When xsrf token is invalid, delegate.resolveCsrfTokenValue returns null, but the return type of SpaCsrfTokenRequestHandler.resolveCsrfTokenValue is not nullable, which causes NullPointerException

To Reproduce

Use the setup in csrf-integration-javascript-spa, and send a post request with invalid xsrf token, the server will throw NullPointerException.

Expected behavior

the server should throw InvalidCsrfTokenException

Possible solution

- override fun resolveCsrfTokenValue(request: HttpServletRequest, csrfToken: CsrfToken): String {
+ override fun resolveCsrfTokenValue(request: HttpServletRequest, csrfToken: CsrfToken): String? {
        /*
         * If the request contains a request header, use CsrfTokenRequestAttributeHandler
         * to resolve the CsrfToken. This applies when a single-page application includes
         * the header value automatically, which was obtained via a cookie containing the
         * raw CsrfToken.
         */
        return if (StringUtils.hasText(request.getHeader(csrfToken.headerName))) {
            super.resolveCsrfTokenValue(request, csrfToken)
        } else {
            /*
             * In all other cases (e.g. if the request contains a request parameter), use
             * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies
             * when a server-side rendered form includes the _csrf request parameter as a
             * hidden input.
             */
            delegate.resolveCsrfTokenValue(request, csrfToken)
        }
    }

Comment From: jzheaux

Hi, @meouwu-dev! I've tried applying the code from the reference and sent an invalid CSRF token; however, I get a 403 instead of a NullPointerException.

Can you please provide a reproducing GitHub sample? That will help get to the bottom of the issue faster.

If you like, you can push a commit to https://github.com/jzheaux/spring-security-samples/tree/gh-14634 to change its CSRF sample to better match what you are experiencing.

What I do after running that sample is the following:

http -a user:password POST :8080 Cookie:XSRF-TOKEN=c6e16183-bc43-40cf-8233-aba3959e2ce7 X-XSRF-TOKEN:c6e16183-bc43-40cf-8233-aba3959e2ce8

Note that the header value is invalid, while the repository is valid. The result is a 403.

Comment From: sjohnr

Thanks for reporting this @meouwu-dev! I was able to reproduce using a missing token scenario (but not an invalid token) and see the issue exists in the Kotlin example. I've fixed this and additionally added some tests to the testing example in the docs as well.