How JWTs Could Be Dangerous and Its Alternatives
Pragati Verma
Posted on December 4, 2022
Introduction
JSON Web Tokens (JWTs) are the most popularly used tokens for web authentication and managing user sessions in modern-day software applications. There is loads of information on the benefits or advantages that JWTs bring to the table, however, very few developers and software architects are familiar with the potential dangers and inefficiencies of using JWT tokens.
In this article, we’ll discuss how JWTs can make websites vulnerable to a variety of high-security threats and attacks if not managed properly. And because JWTs are extensively used in authentication, session management, and access control techniques, these flaws might jeopardize the entire website and its users.
Before we dive into the details, let’s have an overview of JWTs and how they almost become a standard for software developers.
What are JWTs?
JSON web tokens (JWTs) are a standardized format for securely transferring cryptographically signed JSON data across systems. They can potentially include any type of data but are most typically used to transfer data ("claims") about users as part of authentication, session management, and access control procedures.
Unlike traditional session tokens, all of the data required by the server is saved on the client side within the JWT. As a result, JWTs are a common solution for widely distributed websites where consumers must interact seamlessly with numerous back-end servers.
A JWT consists of the following three parts - header, payload, and signature, where the header and payload are base64url-encoded JSON objects which can be decoded from the token to reveal information. The header includes information about the token i.e. metadata such as the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA, whereas the payload contains the user's real "claims." There are no constraints on the payload's content, although it's crucial to note that a JWT is not encrypted.
As a result, whatever information we put in the token is still viewable by anyone who intercepts it. Thus, the security of any JWT-based mechanism is heavily reliant on the cryptographic signature.
The signature, which is a Message Authentication Code (or MAC), is the last component of a JWT. A JWT signature can only be produced by someone who has both the payload (including the header) and a provided secret key.
Because the signature is obtained directly from the remainder of the token, altering a single byte of the header or payload results in an incorrect signature.
It should be impossible to produce the right signature for a given header or payload without knowing the server's secret signing key.
Each part is separated by a dot in the JWT as shown below:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
When decoded, the information can be revealed as follows:
Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payload:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Signature:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
)
As we can see, the signature is really the key part of the JWT!
The signature is what allows a fully stateless server to be certain that an HTTP request belongs to a specific user simply by looking at a JWT token present in the request itself, rather than forcing the password to be sent each time the request is made.
For further user actions, the server merely validates the signed section, obtains user information, and allows the user to do the action. As a result, the DB call is fully avoided.
But there is one more thing you should know about JWT tokens. That is, it uses an expiry period to self-destruct. It is usually set to 5 to 30 minutes. And because it is self-contained, it is difficult to revoke/invalidate/update. This is truly the root of the issue. Let’s look into this in detail in the next section.
How JWTs Could be Dangerous?
Although JWT eliminates the database lookup, it brings security concerns and other complexity to the process. Security is binary—it is either secure or not. As a result, using JWT for user sessions is dangerous.
The biggest problem with JWTs is that the token will continue to work until it expires, and the server has no easy way to revoke it. This could be extremely dangerous in situations such as the following:
Logout doesn’t actually log you out of the system. The JWT token can continue to live for whatever duration is set apart for its expiration even after you have logged out, which means if someone gets access to that token during that time, they can continue to access it until it expires.
Similarly, you can’t block any user from the system for moderation or whatever reason because they will continue to have access to the server until the token expires.
Suppose the user was an administrator who was downgraded to an ordinary user with lower privileges. Again, this will not take effect immediately, and the user will remain an administrator until the token expires.
Because JWTs are frequently not encrypted, anyone who can execute a man-in-the-middle attack and sniff the JWT now has access to your authentication credentials. This is made easier because the MITM attack only has to be carried out on the server-client connection.
Moreover, many libraries that implement JWT have had many security issues. Also, many real-world programs require servers to save the user's IP address and track APIs for rate throttling and IP whitelisting. As a result, you'll need to employ a lightning-fast database anyhow. It's unrealistic to believe that using JWT will render your app stateless.
Alternatives
One typical solution is to keep a database of "revoked tokens" and verify it for each call. If the token is in that revoked list, then prevent the user from performing the next operation. But now you're making an additional call to the database to see if the token has been revoked, which defeats the point of JWT entirely.
The answer is not to avoid using JWT for session reasons entirely. Yet instead, do it the old-fashioned, but time-tested way. Make the database lookup so quick (sub-millisecond) that the extra call isn't necessary by using solutions such as Redis along with JWT such that we can avail the benefits of JWTs but remove most of the security threats discussed earlier.
In this scenario, if the JWT verification is successful, the server will still proceed to Redis and double-check the information there. However, if the JWT verification fails, there is no need to search the Redis database.
Another advantage of this technique is that you may utilize existing JWT libraries on both the front end and back end without developing your own custom way of storing data in Redis.
Conclusion
In this post, we learned what JWTs are and how they are used for authentication. JWTs are simply JSON payloads with an easily verifiable and unforgable signature. We also discussed the vulnerabilities that improper handling of JWTs can introduce and what are the alternatives to use.
That’s all for this article. In case you want to connect with me, follow the links below:
Posted on December 4, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.