CSRF Token keeps refreshing every 2nd request. (Spring boot 3 and Next.JS) - spring-boot

I've been creating a Spring boot 3 and Next.JS app with Spring Security. Been having some problems with the "Set-Cookie" header being refreshed on every 2nd request I make to the backend. I can illustrate the problem as:
Make a request to /finances (with XSRF-TOKEN provided post-successful login)
The request goes through (contains all the necessary data for a valid request [CSRF cookie, JWT token, etc..]). The response contains a "Set-Cookie: XSRF-TOKEN= ...." header without the TOKEN.
I refresh the page (make a new request using the useEffect hook) and the CSRF cookie appears again, a new one.
If i refresh the page (make a new request using the useEffect hook) again, the CSRF cookie is deleted.
And so on.
I pass the cookie value on every request to the backend (whenever it is available) and the backend operates on it correctly. So there are no problems with the token validation.
I've also read that the spring security will provide a new XSRF cookie/value if the request from the client does not provide a cookie of the same name.
Could some one please help?
I've tried configuring the response on the backend to attach a new cookie with the same value, and also saving the cookie value in the session storage for later use. None of the options seem to work.
Making two requests to the server would solve the issue (probably) but that would not be an optimal solution. To the problem.

Related

How are CSRF tokens stored on the server side ( by spring security or tomcat)

This question is not about how CSRF tokens works, but is rather about they are stored on the server side.
In short, CSRF tokens are generated by server and injected in to the web page/form. When the form is submitted the csrf token is extracted by the server and compared to the one saved on the server. So far so good.
From this earlier posting - CSRF token value when same page is opened in two tabs on same machine?
Here's the excellent answer which explains that -
The server will create a CSRF token (token1) and store that token in
the HttpSession. The CSRF token (token1) is also be embedded in the
form on the client side. The client is also given a Session ID
(session-id1) which is stored in a cookie.
When the client submits the form, it sends token1 and session-id1. The
server will then use session-id1 to look up the HttpSession and get
the expected CSRF token for that session. It will compare the expected
CSRF token to token1 and if the values do not match, the HTTP request
will be rejected.
and
If you open the same form in another tab, the browser will still have
access to the Session ID (session-id1). That form will get the same
token (token1) that was associated with session-id1.
In the end, there is only one CSRF token (token1) that is used in both
tabs.
Bit more info can be found in the quoted reference, but it fails to explain the server side part.
The above leads to more questions -
If another form (form2) is opened by user in a different tab - then what CSRF token will it get, will it be same as that for the first form(form1) ?
To explore and unsderstand better, I want to know where and how the CSRF tokens are stored in the backend when using spring security (unlike Session Cookies which are generated by Servlet Container, I am assuming that CSRF tokens are generated by Spring Security module). Is there just one CSRF token per session, which is used accross every form and every tab or there are several CSRF tokens.
Please clarify as much as you can... every drop counts

CSRF token value when same page is opened in two tabs on same machine?

From my understanding, when CSRF is enabled on server side, the server creates a token (say token1) and injects it in to the form and saves the same in the cookie of the client browser.
When the client sends the form request to the server, it sends the csrf token (token1) from browser cookie and also send the same token as in the form. The server validates the request by checking that the token in cookie and the token in form match and then processes the request.
Now, if i open the same form in another tab, will the server generate another token (token2) and inject it in to the form and cookie. Then, in the cookie, token1 will be overwritten by token2. So the submission of the form in first tab will not work in this case? But from experience i see that the submission of form in tab 1 still succeeds.
So can some one explain how it's succeeding in the above scenario??
Since you have added the Spring Security tag, I will describe how Spring Security uses the Synchronizer Token Pattern to protect against CSRF attacks.
the server creates a token (say token1) and injects it in to the form and saves the same in the cookie of the client browser.
That's not exactly what happens. The server will create a CSRF token (token1) and store that token in the HttpSession. The CSRF token (token1) is also be embedded in the form on the client side. The client is also given a Session ID (session-id1) which is stored in a cookie.
When the client submits the form, it sends token1 and session-id1. The server will then use session-id1 to look up the HttpSession and get the expected CSRF token for that session. It will compare the expected CSRF token to token1 and if the values do not match, the HTTP request will be rejected.
If you open the same form in another tab, the browser will still have access to the Session ID (session-id1). That form will get the same token (token1) that was associated with session-id1.
In the end, there is only one CSRF token (token1) that is used in both tabs.
You can find more information about protection against CSRF attacks in the Spring Security reference documentation.

How to pass CSRF token with the RestTemplate

I have two Spring Boot REST applications. One of the applications calls other with Spring RestTemplate. Lets call these applications server and client app.
Server app is sending XSRF-TOKEN token as cookie, this is done for the UI part. However there's no way (None that I know of) for the server to distinguish between the request coming from browser and a request coming from the client app. So I can not selectively send the CSRF token from the server to browser only.
Is there a built in mechanism in Spring which allows RestTemplate to detect CSRF cookie/header and replay the request?
If not how can I do the same manually? Should I wait for the CSRF exception to occur and then read the cookie fro the response and replay it?
If it were to be done once then it would be OK, however to wait for the exception for every RestTemplate call doesn't seem right.
I may try to store the token once and set in from the next time, however how would it deal with the multiple server app scenario (Which I have to implement next). As CSRF token of one server app would be invalid for the other, so I won't be able to store a single token, but I would have to store a map of tokens which would have an entry for every new server app URL.
This all seems too complicated, I would rather have Spring handle it.
Any clues are appreciated.
Thanks
For now I have implemented a REST service without protection in the server app which can be called to get CSRF cookies. I had to allow session to be created always so that CSRF cookie doesn't change in one session and so that I could avoid the error Could not verify the provided CSRF token because your session was not found.
Client app calls CSRF rest service to get the cookies and then sends it with the next service call.
It seems a much to call the CSRF token service for each REST call via RestTemplate however it saves me from implementing complicated logic of storing the session. CSRF token service just delivers the cookies so it's network call doesn't take much time compared to the actual service call.
I was also thinking of using a persistent storage (SQL Database or Redis) to store the CSRF token for the server app and then let the client app directly read it from the persistent storage.
However I couldn't figure out how to associate the Session + Server app + CSRF token together to identify token needed by client. As client doesn't have the session initially, so it can't uniquely find CSRF token for it's token from the database. This method is complicated further by the fact that RestTemplate doesn't store the session for the next call.

How long is Spring temporary CSRF token expiration time?

I enabled CSRF with spring security and it is working as expected.
I read Spring official documentation about CSRF
http://docs.spring.io/spring-security/site/docs/3.2.7.RELEASE/reference/htmlsingle/#csrf
I also read this tutorial about CSRF with Spring and AngularJS
http://www.codesandnotes.be/2015/07/24/angularjs-web-apps-for-spring-based-rest-services-security-the-server-side-part-2-csrf/
What Spring Security does is that it sets up a temporary session for
that. So basically it goes like this:
The client asks a token with an OPTIONS request.
The server creates a temporary session, stores the token and sends back a JSESSIONID and the token to the client.
The client submits the login credentials using that JSESSIONID and CSRF token.
The server matches the CSRF stored for the received JSESSIONID and, if all is green-lighted, creates a new definitive JSESSIONID and a new session-based CSRF token for the client to validate its requests after the login.
As I have understood, when you are not logged in, you can get your first CSRF token by sending an OPTIONS request on any API endpoint, for example /api/login
Spring will then create a CSRF token bound to a temporary session (temporary CSRF and JSESSIONID cookies)
Thus, if I ask the CSRF token than wait a few minutes and finally try to login, the CSRF token may have expîred and I will have to ask another one.
I couldn't find how to configure the temporary Spring session expiration time and I couldn't find what was its exact default duration.
Does anyone has any information about that ?
creates a new definitive JSESSIONID and a new session-based CSRF token
this is a session fixation strategy.
there are at least 2 strategies for CSRFToken generation.
per session
per request
The default behaviour should be per session. It means that as long as session would be alive one and only CSRFToken would be bound to it (but this can be changed).
after successful authentication, because of session fixation, a new session would be created with new CSRFToken.
Thus, if I ask the CSRF token than wait a few minutes and finally try
to login, the CSRF token may have expîred and I will have to ask
another one
this is wrong. it would stay as long as session would be active.
I couldn't find how to configure the temporary Spring session
expiration time and I couldn't find what was its exact default
duration
temporary session is called temporary, because it would be valid until authentication and would be replaced by a new one. But same timeout policy is applied to them as for common session. you can configure session-timeout in web.xml using session-config. the default value of Tomcat is 30 minutes.

Spring Security - REST API - token vs. cookie

I have written a REST- API in Java and I have secured this API with Spring Security. The procedure is like this:
Frontend invokes /login RestService in Backend
Backend gives back token to frontend
at each REST- API Backend invokation the token has to be placed in header
This works fine, but I have read that it is also possible (with Node.JS/Passport.js/Express.js) that the session object with the cookie inside can be transfered out of the box without any custom code.
My question now would be if there is a better approach so that the frontend/client do not need to set the token into the header all the time for any request.
Usually token based authentication has advantages over cookie based.
You can achieve this using middle-ware layer
Here is a good Post - https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/
Server side, I usually first check in the headers if there is an auth token. If not, I then check in the cookies as a fallback.
If you want to use cookies, then at your step 2, you need to add a Set-Cookie header to the response, so that browsers know they must store a cookie. Once done, no need to add a header client-side, since browsers will send cookies each request. You'll need to add a CSRF protection though (here is a good example).

Resources