Why JWT Isn't a Panacea: Understanding Session and Security Issues

META

Activist
SUPREME
MEMBER
Joined
Mar 1, 2026
Messages
118
Reaction score
380
Deposit
0$
b9f309f53f475e1c9fabe6a117ffa26f.png

Introduction​

JSON Web Token (JWT) has gained popularity as a convenient way to authenticate and transfer data between clients and servers. Developers value it for its simplicity, statelessness, and ability to easily transfer user information between services. However, most materials and guides focus solely on the benefits, ignoring important drawbacks and potential security threats.

In this article, we'll explore the main issues with using JWT to store user sessions and discuss more secure alternatives.

What is JWT?​

JWT (JSON Web Token) is a standard token format used to transfer information between two parties in a secure and compact form. A JWT consists of three parts, encoded in Base64 and separated by periods:

a056c0645332dbff313dcb94c0baa29c.png

  1. Header – contains information about the token type and signature algorithm (e.g., HMAC or RSA). Header example:
    {
    "alg": "HS256",
    "typ": "JWT"
    }

  2. Payload – includes claims containing information about the user and their rights. Standard, private, and public claims can be added to the payload. For example:
    {
    "sub": "1234567890",
    "name": "John Doe",
    "iat": 1516239022
    }

    Here sub is the user ID, name is the user name, and iat is the token issue time.
  3. Signature – used to protect the integrity of the token. It is created based on the header and payload using a secret key or key pair (private and public). An example of an HMAC signature:
    HMACSHA256(
    base64UrlEncode(header) + "." + base64UrlEncode(payload),
    secret
    )

    If an attacker changes the payload, the signature will no longer match and the server will reject the token.
JWT is typically used in authentication and authorization processes. After a user successfully logs in, the server creates a JWT and sends it to the client. In subsequent requests, the client transmits the token, confirming its identity and avoiding additional database queries.

Despite its convenience, it is important to understand the limitations and potential risks of using JWT as a session management mechanism.

Why Using JWT for Sessions Is a Bad Idea​

JWT (JSON Web Token) was originally designed for transmitting authorization data between services in distributed systems, not for managing user sessions. However, for some reason, many developers decided that since it's stateless and convenient for APIs, it would also be perfect for user authentication. In practice, this leads to a host of problems, making the system more complex and less secure. Let's explore why JWT is a bad idea for session management.

1. Inability to log out immediately​

The main problem with JWT is that it's self-sustaining. This means that once the server issues a token, it has no further control over it. If the user logs out, the token remains valid until its expiration date. If the token is stolen, it cannot be revoked—an attacker can use it until its expiration date.

Some suggest storing a revocation list, but this completely defeats the purpose of a stateless mechanism, as the server would need to store state again.

2. Outdated data and access rights issues​

The data in a JWT is immutable. If a user's access rights change, their password changes, or their account is blocked, they continue to use the old token until it expires. This creates several serious problems:

  • The user may gain access to resources that they should no longer have access to.
  • Different users in the system may have different levels of access depending on how recent their token is.
  • The system becomes unpredictable from a security point of view.
In traditional sessions, everything is simple – changes are applied immediately because the session information is stored on the server.

3. Token length and redundancy​

Unlike regular session IDs, JWTs contain not only the user ID but also the payload. This makes them significantly longer and creates problems:

  • Long tokens increase network load because they are sent in every request.
  • They may not fit into HTTP request headers.
  • It is inconvenient to store them in cookies or localStorage.
Instead of a simple 32-byte session ID, we get an overloaded token that carries along unnecessary data, most of which is not needed in every request.

4. Vulnerability to attacks​

The JWT contains a payload that, although signed, is not encrypted. Anyone who receives the token can read it. This creates a risk of leaks:

  • An attacker who intercepts a JWT can obtain the user's ID and other data.
  • If the signature algorithm is weak or the key is stolen, the token can be forged.
  • If JWT is stored in localStorage, it is susceptible to XSS attacks.
Traditional sessions with secure cookies (httpOnly, secure, SameSite=Strict) are much more secure.

But isn't JWT convenient and modern?​

Developers using JWT for sessions often cite three key arguments:
it's stateless, it's scalable, and it's secure .
Let's explore whether this holds true in practice.

1. "JWT is convenient because it's stateless!"​

It sounds logical: the server shouldn't store user information between requests, meaning it shouldn't worry about state.
But as soon as a token needs to be revoked (for example, the user logs out, changes their password, or their account is blocked), it turns out that stateful storage is essential.

How do they solve this problem?

  • Revocation lists.
    The server is forced to store data on all revoked tokens and check them for each request. JWT was intended to be a stateless solution, but now we're introducing state again.
  • Short-lived access tokens + refresh tokens.
    Since JWTs can't be revoked, their lifetime is reduced to a few minutes, and refresh tokens allow for new ones to be issued. A great idea, if you don't think about the consequences: now we have two types of tokens, complex refresh logic, and if an attacker obtains a refresh token, they can easily issue a new access token.
  • Centralized token storage.
    Some suggest storing JWT in a database and verifying it with each request. But if you still have to verify the data on the server, why not just use a regular session ID?
Ultimately, any attempt to make JWT manageable necessitates stateful storage .
And if you end up having to track sessions anyway, why use a mechanism that was originally designed without this capability?

It turns out that “stateless” in the case of JWT is either a myth or an illusion that dissipates with the first real case .

2. "JWT is scalable and doesn't require a database!"​

In theory, JWT eliminates the need for the server to store session information. However, in practice, modern databases and caching systems have long solved this problem more quickly and efficiently .

Session ID + Redis:

  • Works faster and more reliably than checking the JWT signature in each request.
  • Allows you to instantly cancel sessions without complex solutions with blacklists.
  • Supports horizontal scaling and fault tolerance.
JWT doesn't offer any unique scalability advantage.
All large systems operating under high load use either sticky sessions or Redis with clustering —and they handle this perfectly well without JWT.

3. "JWT is secure because it's signed!"​

Yes, JWT is signed. But not encrypted .

Which means:

  • Anyone who intercepts the token can read it . Unlike a session ID, which is simply a random string, a JWT contains a payload, which can include a user ID, email address, roles, and sometimes even access rights.
  • If an attacker obtains the token (for example, through an XSS attack or a log leak), they will receive all this information.
  • If someone learns the secret key (signing key), they can forge JWTs and grant themselves any rights .
A signature confirms that the data has not been modified, but it does not protect it from being read .
And if the information in the token is truly sensitive, it should be encrypted , not simply signed, for example, using JWE (JSON Web Encryption) or individual claims.

JWT in microservice architecture​

Despite JWT's problems in the context of user sessions, it can indeed be useful in a microservice architecture . The key point here is that JWT is used not as a session mechanism, but as a means of authorization between services .

What are the benefits of this?

  • API Gateway as a centralized entry point.
    When you have multiple microservices, JWT allows for one-time authentication, and then services can verify the token's signature locally, without constantly accessing the database or a centralized authorization server. This reduces system load and simplifies interactions between services.
  • Additional security mechanisms.
    While JWT alone doesn't protect against compromise, fingerprinting mechanisms can be implemented —linking the token to a device, IP address, or other identifier. This doesn't solve all the problems, but it minimizes the risk of leaks and token use by attackers.
  • Minimizing database requests.
    In microservices, it's important to minimize interservice requests. If every service accesses the database for session validation, this will create additional load. With JWT, it's sufficient to verify the signature locally, without accessing a centralized store.
However, even in a microservice architecture, it is worth considering the disadvantages of JWT :

  • If a token is stolen, it cannot be revoked without additional mechanisms (for example, a list of revoked tokens in a database or Redis).
  • Refresh tokens still need to be stored and verified somewhere, which means it's impossible to completely get rid of the state.
  • JWT is not suitable for sensitive data because its payload is readable by anyone who has the token.

Conclusion​

Every task requires its own tool. If you have a monolithic web application , it's simpler and more reliable to use cookie sessions with Redis .
If you have a distributed microservice architecture , JWT can be a convenient tool for transferring authorization data between services, but be aware of JWT's challenges.
 
Top Bottom