How do I use JWT Authentication in production?
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.
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.
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
payload
of 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
HS256
algorithm, 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
RS256
algorithm, 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
exp
in 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.
Always prefer 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 jti
.
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 Redis
, 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 Redis
with some expiration time. Setting the expiration time is important because if you don't, your token will be there forever (theoretically) and you have to setup a cron-job to remove those tokens.
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, otherwise let them proceed. But don't forget to remove this token on logout.
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
sub
(subject),aud
(audience) andnbf
(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.
TL;DR
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.
Conclusion
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.
Footnotes
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.