JWT - What is the best way to use jsonwebtoken ? I'm lost - node.js

I’m learning to code for one year now. I mainly learned how to deal with a rest API (Node/Express on back-end and Vue on front-end).
I get to the point where I want to develop the ideas I have for app.
For this, I first wanted to develop the backend to have a authentification process that I could use as a boilerplate for other project I would have.
But now I’m completely lost with Jsonwebtoken and how to exactly use it in order to make something secure as well as user-friendly.
So far I understand that a rest API should be stateless (I.e. nothing should be store server-side and should therefore not have DB calls -as for sessions- to grant access to data).
In this respect, I’ve noted different strategies :
Short-lived JWT : (+) this is very secure since you theoretically have to log in every time you want to access the server (-) very bad user experience
Long-lived JWT : (+) user-friendly (persistent login) (-) very insecure (no way to check if JWT was stolen)
Short-lived JWT with Long-lived Refresh Token…
That’s where I get confused…
From every articles/tutorials I’ve read, the refresh token should be linked somehow with a DB (e.g. to store the refresh token key or the blacklisted token…). I’ve also seen a tutorial that partly linked the secret key (to verify the token) with the hashed password stored in the DB. This is kind of smart since previous token will automatically be considered as invalid as of the moment the user changes his password… But this once again mean a DB call…
My point is that I’m coming to the conclusion that
(1) there’s no perfect way to handle authentification process in secure and user-friendly way…
(2) DB calls cannot be avoided to have something a bit secure...
And considering that conclusion, I definitely can’t understand the use of refresh token…
If refresh tokens required DB calls, you could get to the same result with only one main token…
You could for instance store a JWT ID in the token and in the DB… If those two id match upon validation of the token, you issue a new token with a new id that overwrites the previous one… Now if you use an old one, it will never be validated… Then, since you have called the DB to validate the token (most certainly the USER table), you could check in the sametime if, for example, the user is an admin or not (no need to store it in the JWT)… Finally, you could use the « hashed password » trick described above to enhance security…
So… What am I missing ? What is the best strategy ?
I’ll be happy to have your comments on this (or a link to a very good article - I’ve a lot of these though…)
Thank you very much for your help
PS: and I’m not even talking about how to send the token to the server (with cookie but risk of CSRF attach or with header but subject to XSS attack if token is stored client-side)… In this respect I’ve seen multiple tutorial that use JWT through cookie with cerf key stored client side as well as inside the jet => both should be send.
PS2: I hope I'm clear since I'm french-speaking native :-)

So you have asked quite a few questions in this one question. It will be quite difficult for anyone to give a thoughtful answer here, but I shall try. Before that, full disclaimer, that I am the author of a new library called SuperTokens that I believe would provide you the best solution for session management. It's possible that you may have already read our blog: https://hackernoon.com/all-you-need-to-know-about-user-session-security-ee5245e6bdad. It's best if we can chat about this so that I can give you a full detailed explanation about everything you have asked (I would love to help out). Join our discord: https://discord.gg/zVcVeev
Now to answer your question(s):
You should always only use short lived JWTs
Doing a database call for authentication is not a problem, but as everything else, we try and optimise things, so doing fewer db calls, is better. If you go with JWT access tokens and Opaque Refresh tokens, then for most APIs, you do not need to do a db call. However, if you link the secret key of the JWT with the hashed password, then you would have to a db call for every API - which is OK, but I feel unnecessary since you are going to use short lived JWTs anyways (a few hours to a day max)
You mentioned token theft - this is a tricky issue, but according to RFC 6819, you can use the concept of rotating refresh token to detect theft. Of course, actually doing so can be tricky as you have to take care of many race conditions and network failure issues - see https://hackernoon.com/the-best-way-to-securely-manage-user-sessions-91f27eeef460
About why we need refresh tokens: Say you do not have a refresh token, then you would have just one token that you could treat as both, an access and a refresh token. You could still make it short lived (when it's an access token), and then after it expires, treat it as a refresh token - which also has some lifetime. The problem with this is that it's not very clean (point of expiring a token is that it is useless afterwards), and that you are exposing the refresh token over the network every single API call - reducing security. I know you can have HTTPS, but there are HTTPS MITM attacks as well - see the blog post, part 1.
In terms of storage, one neat trick could be to split the access token into two - one to store in secure, httponly cookie, and one to store in localstorage. For every API call, send both to the server (cookies will be sent automatically anyways), and then the server would combine the two and go about authenticating. This would prevent both, CSRF and XSS attacks on sessions!
Now, you could either implement this whole thing on your own, or then use our library that does all these things out of the box: https://github.com/supertokens/supertokens-node-mysql-ref-jwt.
To discuss this more, or any other questions you have, join our discord server.
PS: I know I used this for advertising my lib, but I hope I did answer your question. It is genuinely difficult to give a good explanation to your questions without having a conversation.

Related

JWT: Why Access & Refresh Token

I'm making an app in Node.js & Mongoose which needs to have some way of authentication. I first thought of simple session but then came across JWT's. I read through many articles but I'm still unsure if I should use them. My questions are:
Let's say someone steals a short lived access token that expires in 15 minutes, wouldn't the refresh token be useless, as 15 minutes is a lot of time to do something?
Where & How should I store refresh & access tokens? I guess access in client side memory and refresh tokens in database? But what if a attacker hacked the database and got the refresh token? Does he have acccess then?
Are there any other secure and good ways of authenticate with Node.js?
Thanks in advance!
Short lived token is much better than non-expiring or longer life token so absolutely no to longer life. token loss/theft is a problem that needs dual solution to lock the client to server (ip lock is a bad idea given the ip spoofing) the real solution depends on how deep you want to go like going through a third service establishing trust among both. logout is always possible all depends on as soon the server stops recognising its issued token and client app can code the logic and server rejection.
storing token to persistent storage is not hacker proof unless there is a some sort of transformation like decryption or unwrapping in the server side application layer before storage (so that the token is not as is on client side as stored in the db.).
there are quite few answers present in JWT RFC https://datatracker.ietf.org/doc/html/rfc7519 so using it is reasonably secure for majority of applications. Remember security always demands double checking and two factors in each step but there is always a judgment call that applies.

Is this an acceptable approach to refreshing JWTs?

I'm in the process of rebuilding an existing web app, that uses JWTs to manage authentication. I'm still new to JWTs, so I'm learning about how they should work, while, at the same time, trying to understand why the web app's current implementation is the way it is.
The current version's flow is as follows:
When a user successfully logs in or registers, a user object is returned along with a JWT property. This JWT is included in subsequent API calls as an Authorization header.
Every ten minutes, a get request is made to API endpoint /refresh-token.
If successful, the response body contains a success message, and the response header contains an updated Authorization header.
All subsequent ten-minute timed get requests to /refresh-token use the original JWT that was returned in step 1, and so on.
From what I've read so far, this doesn't correlate with any recommended approaches.
Is it safe enough to replicate this flow in the newer version, or is this something I'm better off not replicating?
Edit: I'm working solely on the front-end - the API isn't being updated for a while, so I'm limited to what it currently returns.
I believe this article summarizes the current state of the art: https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/. You usually have two tokens. Access token which is short lived and an refresh token, which lives longer. This way you don't need to call the auth server every x minutes, but you can do it on demand.
I don't know if you need to deal with blacklisting too? I believe blacklisting is easier when you have a separation of access token and refresh token (only refresh token needs to be blacklisted). But I believe you could deal with this problem too, probably in a bit more sophisticated manner.
Having said that. What you have is not wrong. It's hard for me to point out any flaws in the way you are doing besides of what has been pointed out above.

Is JWT safe if someone knows the secret? If not, how can you make JWT secure?

I recently read a lot of articles talking about how JWT can be used for authentication to improve performance by not saving any session related data. Based on my understanding, it signs the data (usually user_id) with a secret to generate a JWT token. Then each client request sends the token. The server just check whether the signature can be verified and trust what's stored in the payload of the JWT.
My concern is that if someone knows your secret, he can easily create a JWT token himself and pretend to be any user in the system. One simple case is that anyone who can see the source code can easily do that. (eg: internal members)
How do you prevent it from happening? One thing I can think of is to use a randomly generated secret at each sever restart. (this may still not be secure if you sever runs a long time without changing the secret)
Many people seem to have issues with regards to the security of a JWT for this reason, and the inability to white-list/black-list people without losing the benefits from using a JWT. In regards to generating a new secret on each server restart, keep in mind that each time you change the secret, you essentially 'logout' every user who currently is logged in, or for whatever other purpose you are using it for. I think common practice is to just make sure the secret remains just that, a secret. A long, randomly generated string that is kept in a file that extremely few people have access is the best way to prevent a current secret from escaping, as far as I know.
Another thing to keep in mind is that the data is in no way hidden from anyone within the JWT. Anyone can see what you have stored so don't store any sensitive data in there. You probably already knew that from your reading, but it is an extremely easy and fatal mistake to accidentally leave sensitive data in the body of the JWT.

See any security risks with this approach?

I'm working on a RESTful(ish) API that has the following authentication style:
A client calls an "authenticate" API method and passes a username and password over HTTPS POST. This method returns basic account information and a "client token", which is stored on the user account in the database.
All further API calls (all over HTTPS POST) require a client token. If the system can't find the requester by client token, the call is rejected.
My open questions are:
1) Does anyone see a major security problem with this?
2) Is there any good reason why I should have client tokens expire over time or change? Right now I assign a random one to every user. If the user does a logout or forgot password, I generate a new one.
I'd love to know everyone's thoughts on this approach. I'm not going for innovation, I'm just making I'm aware of the risks on this approach.
What you've described is functionally equivalent to a session cookie, only reimplemented in your application, and therefore subject to a number of pitfalls that have likely already been dealt with by most web frameworks.
Ensure your tokens have enough bits of entropy. If the tokens are simple 32-bit integers, wild guesses might be enough to hit on one in use by someone else.
If you're generating these tokens randomly, ensure you use a cryptographically-strong source of random numbers, or the next token might be guessable based on previous tokens.
If these POST requests are coming from scripts and such embedded in web pages, passing the token around as an explicit parameter instead of as a cookie declared secure and httponly makes token-stealing by cross-site scripts much easier.

Signed session cookies. A good idea?

In an effort to increase performance, I was thinking of trying to eliminate a plain 'session cookie', but encrypt all the information in the cookie itself.
A very simple example:
userid= 12345
time=now()
signature = hmac('SHA1',userid + ":" + time, secret);
cookie = userid + ':' + time + ':' + signature;
The time would be used for a maximum expirytime, so cookies won't live on forever.
Now for the big question: is this a bad idea?
Am I better off using AES256 instead? In my case the data is not confidential, but it must not be changed under any circumstances.
EDIT
After some good critique and comments, I'd like to add this:
The 'secret' would be unique per-user and unpredictable (random string + user id ?)
The cookie will expire automatically (this is done based on the time value + a certain amount of seconds).
If a user changes their password, (or perhaps even logs out?) the secret should change.
A last note: I'm trying come up with solutions to decrease database load. This is only one of the solutions I'm investigating, but it's kind of my favourite. The main reason is that I don't have to look into other storage mechanism better suited for this kind of data (memcache, nosql) and it makes the web application a bit more 'stateless'.
10 years later edit
JWT is now a thing.
A signed token is a good method for anything where you want to issue a token and then, when it is returned, be able to verify that you issued the token, without having to store any data on the server side. This is good for features like:
time-limited-account-login;
password-resetting;
anti-XSRF forms;
time-limited-form-submission (anti-spam).
It's not in itself a replacement for a session cookie, but if it can eliminate the need for any session storage at all that's probably a good thing, even if the performance difference isn't going to be huge.
HMAC is one reasonable way of generating a signed token. It's not going to be the fastest; you may be able to get away with a simple hash if you know about and can avoid extension attacks. I'll leave you to decide whether that's worth the risk for you.
I'm assuming that hmac() in whatever language it is you're using has been set up to use a suitable server-side secret key, without which you can't have a secure signed token. This secret must be strong and well-protected if you are to base your whole authentication system around it. If you have to change it, everyone gets logged out.
For login and password-resetting purposes you may want to add an extra factor to the token, a password generation number. You can re-use the salt of the hashed password in the database for this if you like. The idea is that when the user changes passwords it should invalidate any issued tokens (except for the cookie on the browser doing the password change, which gets replaced with a re-issued one). Otherwise, a user discovering their account has been compromised cannot lock other parties out.
I know this question is very old now but I thought it might be a good idea to update the answers with a more current response. For anyone like myself who may stumble across it.
In an effort to increase performance, I was thinking of trying to
eliminate a plain 'session cookie', but encrypt all the information in
the cookie itself.
Now for the big question: is this a bad idea?
The short answer is: No it's not a bad idea, in fact this is a really good idea and has become an industry standard.
The long answer is: It depends on your implementation. Sessions are great, they are fast, they are simple and they are easily secured. Where as a stateless system works well however, is a bit more involved to deploy and may be outside the scope of smaller projects.
Implementing an authentication system based on Tokens (cookies) is very common now and works exceedingly well for stateless systems/apis. This makes it possible to authenticate for many different applications with a single account. ie. login to {unaffiliated site} with Facebook / Google.
Implementing an oAuth system like this is a BIG subject in and of itself. So I'll leave you with some documentation oAuth2 Docs. I also recommend looking into Json Web Tokens (JWT).
extra
A last note: I'm trying come up with solutions to decrease database
load. This is only one of the solutions I'm investigating
Redis would work well for offloading database queries. Redis is an in memory simple storage system. Very fast, ~temporary storage that can help reduce DB hits.
Update: This answer pertains to the question that was actually asked, not to an imagined history where this question was really about JWT.
The most important deviations from today's signed tokens are:
The question as originally posed didn't evince any understanding of the need for a secret in token generation. Key management is vital for JWT.
The questioner stated that they could not use HTTPS, and so they lacked confidentiality for the token and binding between the token and the request. In the same way, even full-fledged JWT can't secure a plain HTTP request.
When the question was revised to explain how a secret could be incorporated, the secret chosen required server-side state, and so fell short of the statelessness provided by something like JWT.
Even today, this homebrew approach would be a bad idea. Follow a standard like JWT, where both the scheme and its implementations have been carefully scrutinized and refined.
Yes, this is a bad idea.
For starters, it's not secure. With this scheme, an attacker can generate their own cookie and impersonate any user.
Session identifiers should be chosen from a large (128-bit) space by a cryptographic random number generator.
They should be kept private, so that attackers cannot steal them and impersonate an authenticated user. Any request that performs an action that requires authorization should be tamper-proof. That is, the entire request must have some kind of integrity protection such as an HMAC so that its contents can't be altered. For web applications, these requirements lead inexorably to HTTPS.
What performance concerns do you have? I've never seen a web application where proper security created any sort of hotspot.
If the channel doesn't have privacy and integrity, you open yourself up to man-in-the-middle attacks. For example, without privacy, Alice sends her password to Bob. Eve snoops it and can log in later as Alice. Or, with partial integrity, Alice attaches her signed cookie to a purchase request and sends them to Bob. Eve intercepts the request and modifies the shipping address. Bob validates the MAC on the cookie, but can't detect that the address has been altered.
I don't have any numbers, but it seems to me that the opportunities for man-in-the-middle attacks are constantly growing. I notice restaurants using the wi-fi network they make available to customers for their credit-card processing. People at libraries and in work-places are often susceptible to sniffing if their traffic isn't over HTTPS.
You should not reinvent the wheel. The session handler that comes with your development platform far is more secure and certainly easier to implement. Cookies should always be very large random numbers that links to server side data. A cookie that contains a user id and time stamp doesn't help harden the session from attack.
This proposed session handler is more vulnerable to attack than using a Cryptographic nonce for each session. An attack scenario is as follows.
It is likely that you are using the same secret for your HMAC calculation for all sessions. Thus this secret could be brute forced by an attacker logging in with his own account. By looking at his session id he can obtain everything except for the secret. Then the attacker could brute force the secret until the hmac value can be reproduced. Using this secret he can rebuild a administrative cookie and change his user_id=1, which will probably grant him administrative access.
What makes you think this will improve performance vs. secure session IDs and retrieving the userid and time information from the server-side component of the session?
If something must be tamper-proof, don't put it in the toddlers' hands. As in, don't give it to the client at all, even with the tamper-proof locking.
Ignoring the ideological issues, this looks pretty decent. You don't have a nonce. You should add that. Just some random garbage that you store along with the userid and time, to prevent replay or prediction.

Resources