I’ve heard people saying that JWT(JSON Web Token) is insecure, try to avoid it and many other things, and they’re not completely wrong. They have fair points against JWT. So, today in this article I’ll show some best practices which I use to make this process much secure.
If you don’t know what JWT is, how it is structured or how it encodes/decodes data, please read this introduction first.
Edit1: I’ve tried to implement all the features in this GitHub repository. Do check it out.
What are the drawbacks?
It is important to make the list of drawbacks first so that we can understand why there are many people not using JWT tokens for authentication.
- Anyone can read the
payloadof a JWT token. If you don’t know this and want to verify, paste your JWT token here and see the output.
- By default, JWT uses
HS256algorithm, which works on the private key encryption. If you’re not the only developer (which is obvious in the production) and have no control over who uses the private key, it can easily be leaked and anyone can generate the token and get the access.
- I’ve also read some blogs that it is possible to brute force HS256.
- If you use
RS256algorithm, which uses public key encryption, you don’t have to worry about any key. But, there are very well knows attacks on these algorithms (e.g. attacks on RSA with PKCS #1v1.5 Padding, RSA with OAEP Padding, Elliptic Curve Diffie-Hellman etc).
- If you generate any token without including
expin the payload, that token becomes valid forever.
- Some people append tokens in the URL and even after logging out of the browsers, that URL gets cached and sometimes, other users who use the same browser can access the cached page.
- Sometimes, there is only a single JWT Authentication server for multiple apps and if it is not properly configured, people can access other apps seamlessly.
- While developing the system, developers store the JWT token somewhere else and also add a second layer to authenticate it. This approach seems quite good but it exposes all the token to the developers and they can misuse the token.
Please educate me if you know some other potential drawbacks of JWT Authentication.
You might be wondering that why one should use JWT for authentication if it has so many drawbacks. But if you’ve read the introduction link given at the beginning, you’ll agree that it is one of the simplest ways of authentication, and we can easily overcome these security threats(because if we can’t, there is no point of writing this blog).
Solution and some best practices which I use!
How to secure a token so that no one can see the token itself.
HTTPS over HTTP to transmit JWT tokens, so that no one can get the original token in between. It reduces the chances that someone extracts the JWT in between and read the payload or reuse the token.
But what if someone found out/stole the token?
But it is still possible that if someone has the physical access to the client’s PC or the client runs any malicious script, the attacker can see the token. So never include any sensitive data inside the payload of any token. If possible, try to generate the primary key using
UUID and only append it with the payload.
How not to make any token valid forever?
As I said, if you don’t add
exp (expiration time), a token becomes valid forever. So always add
exp in your token. Also, try to make JWT token’s expiration time as small as possible. Because if some unauthorized person gets the token, he/she can’t misuse it for long. But, only adding it doesn’t make JWT any secure. You must include
iat (issued at), inside the token. It makes the token somewhat unique. But if you want to be sure that the token is unique and also monitor which token is used to do which type of task, add unique
jti (JWT Token ID).
What if you’re using a single server to authenticate multiple apps?
To prevent the users of some app to access your other apps, include
iss (issuer of the token) in the payload. If you want some users to access only some paths of your app, either add those paths in your token or validate them explicitly in the backend using a unique
If you’re appending the token in a URL (Probably a GET Request)…
First, try to avoid this. Don’t append your tokens in a URL. It makes the token visible to everyone. But still, if you don’t have any choice and want to add the token in a URL, make sure you don’t let browsers cache your page by adding some meta tags, middlewares or whatever it takes. Because that’s not your users’ fault. They had properly logged out, it is you who didn’t properly architect/code your URLs or the whole app. It makes you and your app less credible.
But what if your private key gets leaked?
I guess this is the biggest issue for a developer or a team as you don’t know who leaked the key or sometimes you don’t know whether your key is leaked until it’s too late. For that, I suggest using some other layer for validating the token.
If your app is not handling much traffic, it doesn’t really matter whether you choose
MongoDB or any other way to store your data. But if there is heavy traffic or you want to scale your product later, you can use
Redis. Also Google for the Redis’ benchmarking.
Okay, but how to use another layer to validate your data?
Ideally, you generate and send a JWT token in the response, after a successful login. At that time, before sending the response(before — because if it throws an error, your token becomes invalid in this approach), store this token in
Now if a user makes a new request with a token, first, search for that token in Redis. If you can’t find it there, send the response saying that the user is unauthorized. But if the token is there in your Redis, validate it using your backend because it is possible that it has been expired but not yet been removed from Redis. If your backend says that the token has been expired, send the response with the error and also remove that token from Redis.
There are some cases where you store your tokens in Redis and a user never returns, he/she clears his browser’s storage, start using a different browser or something which makes this token persist forever inside Redis. So don’t forget to set a cronjob (multiple times a day if possible) to remove all the expired JWT tokens.
That’s cool but what about developers? There is a chance, some of them can misuse your token.
A simple solution to this problem is, just make the hash of your JWT token and then store it in Redis. Same goes for the validation — first hash, then search.
Some other precautions
There are 3 different types of claims in a token.
- Registered Claims
nbf(not before) are some other registered claims.
- Public Claims
Which you add on your own. (e.g. user id)
- Private Claims
Custom claims between parties that agree on using them to share the information.
Explanation of all the claims are given in this link in the payload section and details of each element of these claims are described here. As a developer, you must know what are these claims and prevent the collision between these claims.
I personally believe that using JWT authentication alone is not a good idea in a production environment. For the beginners, the setup which I showed might sound a little bit extreme but it makes your product much secure.
It doesn’t matter what you use for the authentication. If you don’t implement the whole system properly, you can never make a secure system.
If you are thinking that still there is a chance that we end up compromising the whole authentication system, please let me know. We can discuss and probably we can help to make the internet more secure.