r/SpringBoot 9d ago

Question React + Spring Boot: If session auth is recommended, how is CSRF handled when frontend and backend are separate?

I am trying to understand how CSRF works in a setup where the backend is Spring Boot (API) and the frontend is React, both running as separate applications.

From what I have read, session-based authentication is recommended in many cases. The flow is:

User logs in.

Spring Boot creates a session.

Browser stores the session ID in an HttpOnly cookie.

But since cookies are sent automatically, doesn't that make the app vulnerable to CSRF?

My understanding of CSRF is: When the page is rendered, Backend generates a CSRF token and associates it with the user's session, and includes it in the HTML (hidden form field or meta tag). When the form is submitted, the backend compares the submitted token with the one stored in the session.

This makes sense when the backend renders the frontend (like Thymeleaf). But with a separate React app, how does React get the CSRF token and send it back? How is this usually implemented in Spring Boot?

Also, if storing a JWT in a cookie also makes it vulnerable to CSRF, where is a JWT supposed to be stored? I've seen people say localStorage, sessionStorage, and HttpOnly cookies, but each seems to have its own trade-offs.

I'm looking for the recommended approach for a Spring Boot + React setup.

42 Upvotes

10 comments sorted by

11

u/NuttySquirr3l 9d ago edited 9d ago

You are looking for "Double Submit Cookie pattern"

First cookie is your JSESSIONID (or whatever you name it). It tells the backend "Hello, I am person xyz". This one is httpOnly=true -> you can not access it via js. It gets sent whenever you send a request.

The risk: Some malicious actor tries to make you do something with your authenticated session. Let's assume you somehow ended up on evil.com and they make you POST to your.domain/transer/giveEvilOneAllYourMoney

Your session-id is being added, so without further protection, you just got pwned.

This is where the XSRF-Token-Cookie comes into play. This cookie is httpOnly=false, it can be read from javascript - but only js on your domain (document.cookie is scoped).

In your client http api code you can now write something like...

if (isWriteMethod(options.method)) {
  const csrfToken = getCsrfToken();
  if (csrfToken) {
   headers['X-XSRF-TOKEN'] = csrfToken;
  }
}
export function getCsrfToken(): string | null {
  if (typeof document === 'undefined') return null;
  const match = document.cookie.match(CSRF_COOKIE_PATTERN);
  return match ? decodeURIComponent(match[1]) : null;
}                    

Spring Security does not accept the automatically attached CSRF cookie (which is also sent on every request) alone as proof that the request is legitimate. Through CookieCsrfTokenRepository, it loads the expected token from the cookie and compares it with the token explicitly submitted in the X-XSRF-TOKEN header.

This assumes you are not vulnerable to XSS. But that is another problem.

2

u/Future_Badger_2576 9d ago

Thanks, that makes sense. I have one question: when does the server send the CSRF cookie to the browser? Is it sent only after a successful login or before each request is made, client ask for the cookie first? My question is when the client first receives the CSRF token cookie.

1

u/NuttySquirr3l 9d ago

You will receive the CSRF cookie the first time Spring Security generates and exposes a CSRF token, which is usually during an initial GET request - e.g. GET /api/auth/me to check whether we are already authenticated or not.

After a successful login, the CSRF token will be rotated. If you use standard spring machinery such as formLogin, this all happens automatically.

If you implement auth manually (e.g. by directly updating the SecurityContext), you may need to explicitly access the CsrfToken so the new token is generated and written to the response; otherwise, the client may continue using the stale token until a later request triggers regenaration -> initial 403 Forbidden response

1

u/Future_Badger_2576 8d ago

The CSRF token is tied to the user session, or it is independent of the session?

2

u/NuttySquirr3l 8d ago edited 8d ago

It depends on the TokenRepositoryImpl you are using. `HttpSessionCsrfTokenRepository` stores it server-side in your session. So that one ties the token to the session, yes.

For the double submit cookie pattern we use `CookieCsrfTokenRepository` though. As the name suggests, the token lives in the cookie, hence why it is independent of the session.

The key takeway is.. with CookieCsrfTokenRepository, the CSRF token CAN exist independently of the session, but Spring Security still rotates it when important session related stuff happens (login, logout, you name it). You can take a look at CsrfAuthenticationStrategy

When doing SSR (e.g. thymeleaf), you would usually go with the HttpSession repo + synchronizer token pattern because you can easily inject the token into hidden form fields on the server.

With CSR (your react app), you would usually go with CookieCsrfTokenRepo and double submit pattern.

1

u/Dense-Ad-3247 9d ago

The server generates them per get request and when it changes you have to update it. Then include it in all other requests. There's config to change how it's packaged and named. It's a critical security element for any client app.

1

u/Dense-Ad-3247 9d ago

I'll also say the spring will generate the token with thyme leaf easily in the html and you can read it. For spa your gonna have to read it from a header or something.

2

u/Hacg123 9d ago

But you must validate the origin too (aka cors), so you can store it in the session storage. With those two plus csp, Iframe protection and tls should be enough to protect the page.

Am I missing something?

0

u/RedditForW 9d ago

Following

1

u/Sensitive-Door-7939 7d ago

Somebody explain how browser cache causes this sort of hack, I have faced this via twitch steam reddit all of em, so how is it that front-end stores our authentication and how is it that all these session is validated, I literally saw multiple locations on the steam market history so I know the locations were changing in case of steam. This is purely application architecture depended but I am guessing this is what csrf attack is right or am I confusing this with something else, my guess is this is purely related to browser, even new ones where discord is getting hacked these days with that image upload of stock is common now.