DEV Community

Cover image for Mastering JWT Security
aymane aallaoui
aymane aallaoui

Posted on

Mastering JWT Security

Ensuring user authentication and protecting an on-going session are vital parts of modern web development. Among the many options for managing authentication and authorization in web applications, JSON Web Tokens (JWTs) have become popular due to their simplicity, efficiency and flexibility. However, just like any other technology, JWTs have their own security considerations. In this article we will explore how to effectively secure JWTs by discussing the best practices.

Understanding JWTs:

Let’s quickly recap what JWTs are and how they work before delving into security measures. They are concise self-contained tokens that consist of three components –header, payload and signature. Generally these tokens are employed for the purposes of authentication or information exchange between parties. After a user has successfully logged in, a new JWT is issued; it then gets sent back to the client who will include it in subsequent requests thus authenticating them.

Best Practices for Securing JWTs:

use HTTPS: this will always guarantee that JWTs which are transmitted are encrypted on the move and hence there is no eavesdropping or man-in-the-middle attack. When HTTP is employed, JWTs become an easy target to intercept and therefore your system becomes insecure.

Keep JWTs Stateless: as opposed to keeping server’s database/session storage with sensitive data/state of sessions, you need to know these tokens are created for being stateless. This way the application can get better scalability and reduce cases of data breaches.

Implement Proper Token Expiry: JWTs should have a reasonable expiration time so that they won’t last long thus giving hacker a little time window. Shorter token expiration times make it hard for hackers who may steal them leading to unauthorized access.

Use Strong and Unique Keys: CRT algorithms like RSA with adequate key length or HMAC SHA-256 are recommended when signing JWTs. Besides, each JTW must always be signed using a unique key to eliminate substitution attacks on tokens.

Validate JWT Signatures: validation must be done in order to confirm whether incoming JTWs were signed by its author or not. Such omissions will lead you into accepting forged or tampered token while threatening your security.

Token revocation: A blacklist or revocation list should be maintained in order to invalidate JWTs where token revocation is necessary i.e. when a user logs out or an account is suspended. This makes it impossible for users to use tokens that may have been compromised or are no longer current to access protected resources.

Do not Store Sensitive Data: Do not put sensitive information which include passwords or personally identifiable data (PII) in the JWT payload; instead, securely store such data on the server and just include a reference or identifier in the token.

Rate Limiting and Throttling: To protect your authentication endpoints from brute force attacks and denial-of-service (DoS) attacks, rate limiting and throttling mechanisms must be implemented. This will prevent malicious actors from bombarding the server with too many authentication requests.

Code Examples

To bring out these best practices, we will walk through some basic code example using jsonwebtoken and nodejs.

const jwt = require('jsonwebtoken');
require('dotenv').config(); 

/* use a secure secret key at least 32 characters long eg '9wJMN71@Dx5#p%bTqY!6Rs*eK$A&zP2H' */

const secretKey = process.env.JWT_SECRET;


const createToken = (payload) => {
  try {
    const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
    return token;
  } catch (error) {
    console.error('Error creating JWT:', error.message);
    return null;
  }
};


const verifyToken = (token) => {
  try {
    const decoded = jwt.verify(token, secretKey);
    return decoded;
  } catch (error) {
    console.error('Error verifying JWT:', error.message);
    return null;
  }
};


const payload = { user_id: 123456 };
const token = createToken(payload);
if (token) {
  console.log('JWT created successfully:', token);

  const decoded = verifyToken(token);
  if (decoded) {
    console.log('JWT verified successfully. Decoded payload:', decoded);
  } else {
    console.log('JWT verification failed.');
  }
}

Enter fullscreen mode Exit fullscreen mode

Conclusion

To effectively secure JWTs, one must have a full understanding of how they function and the various weaknesses that could be attacked. Comply with recommendations such as utilizing HTTPS, enforcing token expiration times and using strong cryptographic implementations; these steps will improve the security of your web apps thus ensuring protection for confidential user information. Always remember that it is an evolving process so you should constantly audit and change your JWT based security strategies according to current threats or best practice. With this kind of behavior that protects against JWT dangers, you can minimize potential hazards and develop confidence within your client base.

Top comments (18)

Collapse
 
faraz_01 profile image
Faraz

Stateless JWT and Token revocation contradicts each other.

Collapse
 
hotgazpacho profile image
Will Green • Edited

How so?

JWTs aren’t ever truly stateless; the iat and exp claims provide a window of time during which the token may be considered valid. That’s state. Revocation lists allow for the token to marked as invalid even before it expires. The longer the validity window, the more important it is to have a means to mark the token invaild.

Collapse
 
danbars profile image
Dan Bar-Shalom

The server has to look for the token in some database to see if it's blacklisted. It means that verification cannot be stateless and based on the token itself only.
"Stateless" in this context means that the server does not have to keep any server storage and look up tokens

Thread Thread
 
hotgazpacho profile image
Will Green

Ok, we share some understanding. If you need to invalidate a token before it expires (the likelihood of which increases with the length of time the token is valid for), then you are going to need to perform some sort of lookup as part of validating the token. A database lookup could be one option. A bloom filter is another. You could also just keep the lifetime of the tokens very short (say, 5 minutes) and accept the trade off of clients needing to obtain a new token more frequently in exchange for reducing the risk of the window of time during which the token can be stolen and used illegitimately.

Thread Thread
 
teckel12 profile image
Tim Eckel

I often hear this about JWT, make sure it's stateless and maintain a blacklist/revocation list, which are totally contradicting terms, you can't have both. The overhead of abandoning stateless is typically extremely small, so don't even go that route unless required.

There are situations where stateless is the ONLY way possible, for example, if you're calling something that doesn't have access to the database, like a 3rd party service or microservice with no database. That's where stateless is useful. But, if this is (for example) an API service that has database access anyway, just abandon the stateless "feature" of JWT, as you'll probably need to validate the token anyway (with a blacklist), so some type of database access will be required anyway.

Thread Thread
 
shipanliu profile image
Ted

short explain: In scenarios where you need to authenticate to a service that doesn’t have direct access to your user database, JWTs are ideal because they can be validated by the service independently as long as it has the right key or public key to verify the token’s signature.

In a microservices architecture, services often need to authenticate requests from other services or components. Since each microservice can validate the token by itself, there's no need for a centralized session store or database lookups.

Collapse
 
viiik profile image
Eduard

What they mean is server side state. It's impossible to validate jwts allowing for revocations without checking server state.

The main benefit of jwts is authentication without server state. If you want to allow revocation you lose that benefit.

Thread Thread
 
teckel12 profile image
Tim Eckel • Edited

Being stateless is just one feature of JWT, and it can be very useful when you're talking with a service that doesn't have database access to validate access. This is where the stateless feature shines. For example, you can create a very short-lived token (like 30 seconds) to sign on to a 3rd party service that doesn't have access to your database. But if you have database access, just abandon the stateless feature, as the overhead is small to access the database, and you should be anyway to makes sure the token isn't on a blacklist.

Thread Thread
 
shipanliu profile image
Ted

The server uses the information encoded within the token to identify the user and doesn't keep any session information on the server. This is the typical usage of JWT. Even though JWT is designed to be stateless, you can implement a stateful mechanism (like token blacklisting) by storing the token's identifier in a database and checking it on each request to ensure it hasn't been revoked.

Collapse
 
shipanliu profile image
Ted

The suggestion to abandon the stateless nature of JWT if you have database access oversimplifies the situation. Even if you have database access, the stateless nature of JWT can still provide benefits, such as reducing the frequency and load of database queries for authentication and authorization, thus improving performance and scalability.

Collapse
 
setanjan123 profile image
Setanjan Roy

Depends on how you implement it. There is a pretty common design pattern of using issuing two JWTs at login. One a short lived access token and the other a long lived refresh token. You can store the refresh token in a database and validate against it. The refresh token can only be used to issue new access tokens and not access other APIs. So your access token is still stateless. And you can easily revoke the refresh token by deleting it from the database.

Collapse
 
rd162 profile image
R.D. .

No. It quite simple to implement. You just need to keep revoked tokens in DB or in-memory cache (like Redis), removing it when expire. It had better to implement this check at API Gateway layer to avoid each service perform the revocation check.

Collapse
 
fridaycandours profile image
Friday candour • Edited

Am not seeing how you are verifying identity here. How is this mastering? Be a little more elaborate please.

Collapse
 
juni0r profile image
Andreas Korth

Using JWT for session management is a gross misappropriation of the technology. This has been pointed out countless times, in write-ups as well as the comments on this post, so I'm not going to reiterate here. If you're unable to understand the fundamental contradictons that inevitably arise, there's ample material available to educate yourself. If you still don't understand and publish articles about "mastering" something that is a fundamental mismatch, you should rethink your career choices. And no, the fact that even popular frameworks use JWT doesn't make it any better.

Collapse
 
thesuhu profile image
The Suhu

You keep stateless but in another hand you want revocation, it's contradict

Collapse
 
manchicken profile image
Mike Stemle

I dig it. I think it's essential to sign JWTs in order to maintain the integrity of the token. This is a decent write-up, thank you!

Collapse
 
shipanliu profile image
Ted

The term "stateless" in the context of JWT (JSON Web Tokens) refers to the characteristic that the token itself contains all the necessary information to validate the session or request

Collapse
 
shipanliu profile image
Ted

Unlike traditional session management, which often stores session information on the server (like in a database or memory), JWT does not require the server to keep session state. The server decodes and validates the token using a secret key or a public/private key pair