How to Secure JWT Tokens: Best Practices Guide

JSON Web Tokens (JWTs) have become the industry standard for stateless authentication. They allow servers to verify a user's identity without storing session data on the server itself. However, this stateless nature introduces specific security risks if not implemented correctly. In this guide, we will explore how to properly secure JWT tokens to protect user data and maintain application integrity.

Understanding the JWT Structure

Before diving into security measures, it is crucial to understand the three parts of a JWT: the Header, the Payload, and the Signature.

  1. Header: Contains the algorithm used to sign the token (e.g., HS256, RS256).
  2. Payload: Contains the claims (data about the user, like ID and role).
  3. Signature: Ensures the token hasn't been tampered with.

Because the payload is base64-encoded, it is readable to anyone who intercepts the token. This is why security measures like HTTPS and signature validation are non-negotiable.

1. Always Use HTTPS

The most fundamental step in securing any token is ensuring it is transmitted over a secure channel. If a JWT is sent over HTTP, an attacker on the network can intercept and read the token payload, potentially extracting sensitive user information.

BBVA emphasizes that using HTTPS is mandatory to prevent man-in-the-middle attacks. This encryption ensures that the token remains confidential from the moment it leaves the client to the moment it arrives at the server.

2. Secure Your Signing Secrets

The signature is the only part of the JWT that proves the token is authentic. To generate a valid signature, you need a secret key (for symmetric algorithms) or a private key (for asymmetric algorithms).

Do Not Hardcode Secrets

One of the most common mistakes developers make is hardcoding the secret key directly into the source code. If your code is pushed to a public repository (like GitHub), the secret is exposed. H2S Media advises against this practice, recommending instead to use environment variables to store secrets. This keeps sensitive data separate from your application logic and prevents accidental exposure.

Rotate Secrets Regularly

Even if your secrets are stored securely, they should be rotated periodically. If a secret is compromised, an attacker can forge tokens indefinitely. Regular rotation limits the window of opportunity for an attacker.

3. Choose the Right Storage Mechanism

Where you store the JWT on the client side is a critical security decision. The two primary options are LocalStorage and HttpOnly Cookies.

The Risks of LocalStorage

Storing JWTs in localStorage makes them accessible to any JavaScript running on the page. If your application has a Cross-Site Scripting (XSS) vulnerability, an attacker can easily steal the token and impersonate the user. MakeUseOf highlights that while localStorage is convenient, it is vulnerable to XSS attacks.

The Security of HttpOnly Cookies

HttpOnly cookies are not accessible via JavaScript. This makes them immune to XSS attacks that attempt to steal the token. For maximum security, especially in applications handling sensitive data, storing the JWT in an HttpOnly cookie is the recommended approach. This method also helps mitigate CSRF (Cross-Site Request Forgery) attacks if combined with proper SameSite attributes.

4. Implement Short Expiration Times

JWTs are designed to be stateless, meaning they are valid until they expire. However, the longer a token is valid, the higher the risk if it is intercepted. AWS recommends using short expiration times for access tokens. By keeping the TTL (Time To Live) short, you limit the damage an attacker can do if they manage to steal the token.

The Refresh Token Pattern

To maintain a seamless user experience without increasing security risks, developers often use a combination of short-lived access tokens and longer-lived refresh tokens. The access token grants immediate access to the API, while the refresh token is used to obtain a new access token. Refresh tokens should be stored securely, often in HttpOnly cookies, and have a longer expiration time.

5. Handling Token Revocation

One of the inherent challenges of JWTs is that they are stateless. Once issued, a valid token remains valid until it expires, even if the user logs out or changes their password. This makes revocation difficult.

Short TTLs as a Revocation Strategy

As mentioned, the most effective way to handle revocation is to keep the expiration time very short. If a token is compromised, the attacker only has a limited time to use it.

Blacklists

For applications that require immediate revocation, you can implement a stateful blacklist. When a user logs out or their session is invalidated, you add the token's ID (JTI) to a database or cache. Before validating a token, the server checks if the JTI is in the blacklist. However, this approach requires maintaining state, which contradicts the stateless nature of JWTs.

6. Validate Token Claims

When receiving a JWT, your server must perform rigorous validation. AWS details the importance of validating the Issuer (iss), Audience (aud), and Expiration (exp) claims.

Failing to validate these claims can lead to authorization bypasses, where a user gains access to resources they shouldn't have.

7. Consider Alternatives like PASETO

While JWTs are widely used, they have a history of security flaws. For example, the "none" algorithm vulnerability allowed attackers to forge tokens. HackerNoon discusses the rise of PASETO (Platform for Authentication, Security, and Encryption of Tokens), a newer token standard designed to address JWT vulnerabilities. PASETO supports both symmetric and asymmetric keys and is designed to be more secure by default. While JWTs are still the standard, understanding alternatives like PASETO is crucial for architects making long-term decisions.

Frequently Asked Questions

What is the difference between HS256 and RS256?

HS256 (HMAC with SHA-256) uses a shared secret key between the client and the server. RS256 (RSA with SHA-256) uses a private key to sign the token and a public key to verify it. RS256 is generally considered more secure for distributed systems because the private key never leaves the server.

Can I revoke a JWT immediately?

Because JWTs are stateless, you cannot technically revoke a JWT immediately without maintaining a blacklist or using short expiration times. The most practical solution is to use short-lived access tokens.

Is LocalStorage safe for JWTs?

No, LocalStorage is vulnerable to XSS attacks. If your application is compromised, an attacker can read the LocalStorage and steal the JWT. HttpOnly cookies are the recommended storage mechanism for security.

Conclusion

Securing JWTs requires a multi-layered approach. By using HTTPS, managing secrets securely, storing tokens in HttpOnly cookies, implementing short expiration times, and validating claims rigorously, you can significantly reduce the risk of token theft and misuse. As the landscape of web security evolves, staying informed about best practices and alternatives like PASETO is essential for maintaining a robust security posture.

References

  1. JWT vs PASETO: What's the Best Tool for Generating Secure Tokens?
  2. How to Build a Secure Nest.js REST API Using JWT and MongoDB
  3. How to secure API Gateway HTTP endpoints with JWT authorizer
  4. Best of 2021 – How to Revoke JSON Web Tokens (JWTs)
  5. JSON Web tokens (JWT): how to use them safely
  6. How to Create a JWT Secret in Node.js (Best Practices + Examples)
  7. How to monitor, optimize, and secure Amazon Cognito machine-to-machine authorization