JWT auth in cookies with stateless server and no server side rendering - node.js

I am trying to implement jwt in cookies for auth on a single page application react front end which communicates with various node microservices running express.
I am doing this as it appears storing the jwt in sessionstorage makes the app vulnerable to XSS.
However, by using cookies, the apis are now vulnerable to csrf attacks.
Traditionally, csrf attacks are mitigated by creating a csrf token, storing it in a server session, then rendering it in a hidden form field.
Then, upon submitting the form, the value of the csrf token is checked against the server session value to check they match.
I cannot use this approach as:
- servers are stateless
- have no server side rendering.
So I am confused as to which csrf method I should employ.
I have read about double submit method, where you submit a csrf token on every ajax request, and have the same value stored in a cookie, then the server checks both for a match.
However, I cannot get the intial csrf token into the html in the first place as there is no server side rendering.
What is the best practice for achieving jwt in cookies with csrf protection in a stateless architecture with no server side rendering?

Simply don't store the JWT token in a cookie
CSRF attacks are possible because browsers will send cookies with HTTP requests, even if they are initiated by a script running on a 3rd party site. Thus evilsite.com might send a DELETE http://yoursite.com/items/1 request to your web service. This endpoint requires you to be logged in, but because the browser will send any cookies stored for yoursite.com, if authentication is cookie based then evilsite.com can piggy back on your authentication method and call authenticated methods that your user didn't intend to call on their behalf.
However, authentication doesn't have to be cookie based. If you are creating a client-rendered JavaScript app, then it is simple to send the authentication token as an HTTP header rather than in a cookie. If you do this then it is impossible for evilsite.com to make use of your token (they can only use tokens stored in cookies), and you never have the problem in the first place.

Related

CSRF Tokens without server side rendering

I am looking for a way to prevent CSRF attacks, but am confused about how to pass the CSRF token to the client. I would like to avoid inserting the token into the HTML because I want to minimize server side render and opt for statically served pages instead. For authentication, sessions with cookies are being used.
So how does one get the CSRF token to the client then? I read about the double posting strategy, but read it was less secure than inserting the token into the html server side. Could an alternative be sending a separate authenticated GET request to the server after the page is served to request a CSRF token? The server then stores the CSRF token in the user's session and sends it back to the client.
Sorry if this is a silly question, but I want to be sure :)

is it possible to only recieve cookies on a particular route, rather than all the routes?

I'm implementing an auth service, where I'm using the concept of access and refresh token. Storing refresh token as httponly cookie on the client side. I want to ask, how can i make only certain api routes to have cookie in their req. like, the refresh JWT token should only be sent over /auth/refresh route only.
Cookies have these options to control when they are sent, which you can set when creating them:
Domain: auth.mycompany.com
Path: /auth/refresh
So it is really a sesign question to structure server routes accordingly. Cookies cannot support multiple paths.
Generally OAuth cookies are sent from front channel requests (browser redirects) and not used for back channel requests (Ajax calls) where the browser is likely to drop them anyway.
I would avoid building to an Auth server yourself - it is a complex component - aim to use a free system such as Curity Community Edition or Keycloak instead.

If the JWT token for auth is saved in the HTTP-Only cookie, how do you read it from the cookie so that I can include it in request header?

I am building a Node web app using JWT for user auth.
My server sends JWT in HTTP-Only cookie via 'Set-Cookie' when user submits correct id and password, but I am stuck on how to access that JWT stored within the cookie so that I can include it in the Authorization header when making authorized API requests.
How can I access the HTTP-Only cookie from client-side to include it when sending API request to server?
Or, would it be safe to just not use the cookie at all by having the server send the JWT in response body? So that I use the JWT by putting it in a client-side variable? That way, I believe the variable is only alive until the user closes the browser.
I have looked for many resources, but was not able to find clear answer to this issue that I am stuck with.
While there are numerous ways to solve this problem, I will suggest the method where the client sends the JWT twice on each request: in an HttpOnly cookie, and also an an Authentication: header.
Let's examine what security issue each is solving:
HttpOnly Cookie
From the Mozilla documentation:
To help mitigate cross-site scripting (XSS) attacks, HttpOnly cookies are inaccessible to JavaScript's Document.cookie API; they are only sent to the server.
This is to mitigate cross-site scripting risk; imagine your website has an XSS vuln -- for example I can put <script>some_code{..}</script> into a comment, and any user who views my comment will have my code running in their browser. Yes, the attacker's code is running inside the victim's logged-in browser, but because of the HttpOnly flag, it can't extract the session cookie and send it to the attacker.
Authentication header
The problem with cookie authentication is that the browser will automatically attach them to any request to the domain that the cookie belongs to, this allows cross-site request forgery attacks. To prevent this, the usual method is to send the session token (in your case a JWT) to the client in a method other than a cookie, ie in a different header, or maybe in a hidden HTML field.
If the client is able to echo the JWT token back to you on their next request, that means they were able to read the previous response, and are not an attacker doing a blind injection CSRF attack.
Overall Suggestion
Combine the two!
The two methods serve different purposes: HttpOnly cookies protect against XSS attacks while Authentication headers protect against CSRF attacks.
There are many ways to combine them, but they all boil down to putting the JWT in some sort of authentication header, and putting a sessionID in the cookie, and having the server check that these belong to the same session. Important: remember that an attacker who achieves XSS on your site will be able to read the JWT, so for the cookie to be doing its job, the cookie should be a separate value that is not contained in the JWT. (ie if the attacker can figure out the right cookie value by looking at the JWT, then the cookie is not providing any security).

after successful login the user is redirected to the home page where the SPA loads. Is using session cookie with JWT a bad idea?

I have an express + postgres backend, and I'm using passport-facebook for FB oauth.
If a user hits my app at / without having a valid token in localStorage, they're taken to /login.
My /login page (where you're greeted with the familiar "Continue with Facebook" message) is server rendered (for various reasons). Upon clicking this button, I either verify the user if they exist and send them a session cookie with the initial JWT, or create a new user and send them a session cookie with the initial JWT. In both cases, the success condition is that they are redirected to / and served the SPA assets.
One of the first things the SPA does is take the JWT from the session cookie and put it into localstorage, and then deletes the cookie.
Is this a terrible approach, or is it valid in my use case?
You are using the session cookie as a means to store the JWT on the client. This means that you create a server session that will remain open until it expires, by spending server resources
Session cookies are not readable from javascript, so I guess you make a request to the server to get the JWT, right?
The process does not seem problematic, but I think you could optimize it by sending in your redirection process directly the JWT in the response using a regular cookie with set-cookie header. The cookie will be stored in client and you could access it directly

Authorization (and security, in general) for web and mobile apps of the same service

Help me to understand how to implement proper security for web and mobile apps, which would be enough for my case.
What I have:
Backend. Some sort of Stateless REST API, which consumes and produces JSON text. Does not store any kind of state.
Web application. Main portal to the service functionality.
Mobile applicaiton. Provides a reduced a set of functions to users of the service
I am not going to store any state on the backend. Instead, I am going to delegate this to both mobile and web browser applications.
Now here comes the question of security. How do I properly secure that?
Since session mechanism does not really work for me, I decided to go with JWT.
In my JWT I am going to store user Id and some other information like, for instance, user's privilegies.
For mobile app, I am going to send this token as a part of a response and the app will store it inside its secure store.
Each request it will send this token as Authorization Header.
For web app, I am going to send this token via HttpOnly cookie. This token, thus, will be included in every request from the client.
The problem now is a possible CSRF-attack. To address that I thought of the following. Each user "session" will be associated with CSRF token.
Since I can't store this token on the server (remember, stateless API), I can send it as encrypted (again, with JWT) token to the client via HttpOnly cookie and non-crypted in a regular cookie.
Now, every request the web client will use non-crypted token from the cookie and send it back to the server. The server will check if this token matches from the Encrypted one which is stored in HttpOnly cookie.
Also, I am going to use different URL endpoints for web and mobile web apps. What for? In order to keep auth mechanisms described above separate - I believe this will help me to keep the service secure.
Do you think it is an OK solution? What problems do you see here?
Thanks in advance.
In general, what you described looks good and pretty standard. However, if I understand correctly, the CSRF protection is flawed.
To make sure I understand correctly: a csrf token would be stored in an encrypted httpOnly cookie, only to be sent back to the server as reference. Another cookie would have the same value but unencrypted, in a plain (non-httpOnly) cookie, and the server would compare these two. What's the point? An attacker would still be able to create a webpage to have a user make an request to your website, and both cookies would still be sent.
The reference cookie is ok to be in the httpOnly cookie for reference, but the other one should not be a cookie. It could for example be a request header value that you add to all requests. The client could receive it in a response, but not as a cookie. With jQuery in the web app, you can use the beforeSend hook to add it to all subsequent requests as a header. This way an attacker could not make valid requests from another domain.

Resources