JWT is gaining popularity these days. At ZAPR, we use JWT for user session management, mainly for authenticating requests. This blog post focuses on trade-offs of choosing JWT and nitty-gritty things we learned while implementing it. If you want to know about specifics of JWT, please read this.
Why we considered it?
- JWT tokens are inherently stateless. It reduces dependency on another system. No bottleneck issues.
- Since there is less dependency, we can easily scale at the application server level rather than authentication server.
- It can be used for Single Sign On, due to its self-contained nature.
- JSON. As the name suggests, it encodes JSON, which enjoys good support in all languages and frameworks.
Sounds great, but no new technology comes without its own set of disadvantages. Before implementing in production, it is important to know them and take a call.
Its stateless advantage is sometimes its disadvantage.
- If a token is compromised, there is no way to blacklist it. Two ways to go about it:
- Maintain a list of blacklisted tokens and cross-check against the list, but it destroys stateless advantage.
- Change the secret. All users would be logged out, which is very inconvenient and bad experience if blacklisting happens very frequently.
- JWT Token expiry cannot be extended without changing the token. Since JWT contains all the information within itself and token is created on the payload, changing the expiry time would change the token.
- Tokens are generally longer in length than a session id. It increases the Round Trip Time (RTT) of requests.
- If you are worried about replay attacks, JWT doesn’t provide any protection, inherently due to its stateless nature. However, you can use a claim called “jti” for token ids, but would lose the stateless advantage.
After analyzing the trade-offs, we still decided to go ahead with JWT. When we implemented and used it over a period of time, we picked up few things which we think everyone should keep in mind while implementing it:
- There is a difference between encoded JWT and encrypted JWT. Do not store any Personally Identifiable Information (PII) in it. Even if it’s an expired token, once it gets in the wrong hands, an attacker just needs to base64 decode the payload part.
- If you are concerned about the authenticity of the token, make sure that server marks an unsigned token as invalid. Since an unsigned token is considered valid, anyone can change the payload, remove the signature and send it to the server. This is also called signature stripping attack.
- Algorithm for signing is in the header, which can be read by anyone (refer point 1). Potentially an attacker can change the algorithm and use it to their advantage. Signing algorithms should be explicitly supported by the server.
- JWT can be created such that it never expires. Imagine a never-expiring access token! Use separate secret(s) in Production Environment.
- JWT takes care of its payload and nothing else. Before using it over public network,
- Ensure only encrypted tokens are accepted, or,
- Serve it over an encrypted channel such as HTTPS connection.
- If your token is used by multiple services and all the services are not under your control (probably external services), do not use HMAC algorithm. HMAC uses a single key for signing and verification. If the service goes rogue, it can generate a token on its own. Use asymmetric algorithms such as RSA and ECSA where different keys are used for signing and verification.
It may seem that JWT has a lot of short-comings and things to watch out for. To be fair, every new mechanism has its own advantages and disadvantages. JWT is really useful when you want stateless behaviour. The idea is to give the entire picture of what tags along with JWT so that one can make an informed decision.
Do let us know in the comments section the challenges you faced either at a standard level or at an implementation level of JWT