How to protect JWT Refresh Token Strategy with AES and RSA Encryption

custom refresh token flow
custom refresh token flow

A Secure Approach to JWT Refresh Tokens Using RSA and AES

TLDR;

In modern web applications, securing user sessions across multiple devices while ensuring seamless authentication experiences can be a complex challenge. One solution that balances security and usability is the use of a refresh token mechanism in conjunction with JWT (JSON Web Tokens). In this blog, we’ll explore how this mechanism is implemented securely using both RSA and AES encryption.

JWT and Refresh Tokens: The Basics

JWT tokens are widely used to authenticate users across API requests, carrying essential user data in a signed and encoded format. However, they come with an expiration time, requiring users to reauthenticate. A refresh token solves this issue by allowing users to request a new JWT without the need to log in again, thereby improving the user experience.

However, refresh tokens present their own security challenges. If a refresh token is compromised, an attacker could potentially generate new JWT tokens indefinitely. That’s why a robust system for securely handling refresh tokens is critical.

Mechanism Overview

This solution involves two key layers of encryption:

  1. RSA encryption to protect sensitive keys.
  2. AES encryption for encrypting the actual refresh token and device ID.

The process is broken down into two stages: generation and validation/refresh.


Step 1: Client-side Encryption

  • The client generates a random AES key, encrypts the refresh token and device ID using this AES key, and then encrypts the AES key itself using the server’s RSA public key.
  • The client sends the encrypted AES key, refresh token, and device ID to the server.

Step 2: Server-side Decryption

  • Step 1: The server decrypts the AES key using its private RSA key.
  • Step 2: With the decrypted AES key, the server decrypts the refresh token and device ID.
  • Step 3: The server validates the data, including the token’s expiration, the JWT’s JTI, and the device ID. If the data is valid, the server generates a new JWT and refresh token.

Step 3: Refresh Token One-time Use

  • After validation, the server generates a new refresh token and issues it back to the client. The original refresh token is invalidated, ensuring that refresh tokens can only be used once.

Security Considerations:

  • AES encryption secures sensitive data like the refresh token and device ID.
  • RSA encryption ensures that only the server can decrypt the AES key, adding an additional layer of protection.
  • One-time use refresh token prevents token reuse, reducing the risk of attacks if a token is intercepted.

Security Benefits and Considerations

  1. Minimized Risk from Refresh Token Theft: By encrypting both the refresh token and AES key, this method ensures that even if the refresh token is stolen, it cannot be used unless the attacker also gains access to the private RSA key.
  2. Fine-Grained Session Control: Storing the refresh token and its metadata in Redis allows for the revocation of tokens on a per-device or per-session basis. This gives users and administrators the ability to log out devices individually or all at once.
  3. RSA and AES Combination: RSA encryption is used to secure the AES key, while AES encryption provides the speed and efficiency to handle the encryption of larger pieces of data (like refresh tokens).
  4. Token Expiry Management: The JWT itself can have a long expiration period (e.g., 10 mins), while the refresh token’s expiration is managed through Redis (e.g., 7 days), ensuring that session duration is determined by activity rather than fixed time limits.

Conclusion

This refresh token mechanism offers an efficient and secure way to manage user authentication across devices, combining the best of both RSA and AES encryption to ensure that sensitive tokens are protected. The dual-encryption approach, combined with Redis-based token management, ensures that user sessions are both secure and flexible.


Security concerns

The refresh token mechanism appears to follow good security practices, but let’s break it down and evaluate its security to ensure it’s robust enough. Here are some key points and concerns:

1. AES and RSA Encryption

  • Strengths:
    • Using AES (Advanced Encryption Standard) to encrypt the refresh token and device ID is a solid choice. AES is a symmetric encryption algorithm known for its speed and security, especially with a 256-bit key.
    • RSA (Rivest-Shamir-Adleman) encryption for the AES key adds an extra layer of protection. By encrypting the AES key with RSA, you’re ensuring that only the server (which holds the private key) can decrypt the data, keeping it secure during transit.
  • Potential Risks:
    • Key Management: It’s crucial to securely manage the RSA private key on the server side and the public key on the client. Any compromise of these keys would expose the entire system.
    • Performance: RSA is relatively slow, and the key size limitation (typically 2048-bit or 4096-bit) can introduce latency if large amounts of data need to be encrypted or decrypted frequently.

2. Refresh Token Usage

  • Strengths:
    • Short-lived JWT, Long-lived Refresh Token: This is a common and secure pattern. The short JWT lifespan reduces the exposure window if a token is compromised, while the refresh token allows seamless reauthentication.
    • Device-level Tracking: By tying the refresh token to a specific device (using a device_id or another unique identifier), you reduce the risk of token misuse across different devices.
  • Potential Risks:
    • Token Theft: If the refresh token is stolen (e.g., via an XSS attack or by a malicious actor gaining access to a client device), an attacker could potentially get a new JWT. Even with encryption, the client is responsible for protecting the refresh token. Implementing secure storage mechanisms on the client-side (such as using secure storage for mobile apps or HTTP-only, secure cookies for web apps) is critical.
    • Replay Attacks: The strategy already invalidates the refresh token after a single use, mitigating this risk. However, care should be taken to ensure the server tracks the refresh token’s JTI and device ID and invalidates tokens properly, preventing any token reuse or replay.

3. Redis for Token Storage

  • Strengths:
    • Granular Revocation: Storing JWT and refresh tokens in Redis allows you to revoke tokens by user and device, offering fine-grained control.
    • Fast Lookups: Redis is an in-memory database, making it fast for checking token validity and managing token sessions.
  • Potential Risks:
    • Data Loss: If Redis is not configured with persistence or high availability, tokens could be lost if Redis crashes or is restarted. You might need to ensure that Redis is backed up or replicated to avoid this risk.
    • Scaling Issues: While Redis is fast, storing tokens for a large number of users across multiple devices can grow the Redis storage significantly. You should monitor the size of stored data to avoid performance degradation or memory issues.

4. Security Concerns and Mitigations

  • Refresh Token Lifetime: A long-lived refresh token can be risky. If a token is compromised, an attacker could potentially use it for an extended period. To mitigate this:
    • IP and Location Checks: You could add extra layers of verification by checking the IP address, location, or other identifying features of the user’s request before issuing a new JWT.
  • Rate Limiting: Implement rate limiting on refresh token requests to prevent brute force or abuse attempts.
  • Secure Transmission: Ensure that all refresh tokens are transmitted over HTTPS to prevent interception (MITM attacks).

Conclusion: Is It Secure Enough?

My solution appears to be secure enough for most use cases, especially with strong encryption (AES + RSA), device-bound tokens, and Redis for token revocation. However, no system is 100% foolproof, and there are always areas to enhance:

  1. Consider Refresh Token Rotation: Rotating tokens on every use makes it harder for attackers to reuse stolen refresh tokens.
  2. Rate Limiting: Implementing rate limiting for refresh token requests will mitigate brute-force attacks.
  3. Monitor and Audit: Keep logs of refresh token use and look for abnormal patterns to detect misuse.
  4. Key Management: Ensure secure key storage and consider using hardware security modules (HSMs) for key management.
  5. Session Handling: Ensure sessions are handled securely to avoid hijacking.

If you implement these best practices, your system should be secure enough to mitigate most common attack vectors, while providing a user-friendly experience.

Inspired: https://stackoverflow.com/a/26834685