Why JWTs Suck as Session Tokens

Michael Chung

http://cryto.net/~joepie91/…

Existing session implementations (eg. express-session for Express) have been running in production for many, many years, and their security has been improved a lot because of that. You don’t get those benefits when using JWT tokens as makeshift session cookies - you will either have to roll your own implementation (and most likely introduce vulnerabilities in the process), or use a third-party implementation that hasn’t seen much real-world use.

javaghost

I cannot say about other languages,
but in Java using JWT gives you significant performance gain.
Loading/Creating Session (in Tomcat container) object is costly.

If we store the ID in a cookie, our total size is 6 bytes. If we store the ID in a JWT (with basic header fields set, as well as a reasonably long secret), the size has now inflated to 304 bytes. For storing a simple user session, that is a ~51x size inflation on every single page request in exchange for cryptographic signing (as well as some header metadata).

This neglects the 22 bytes of a session token. Thus the comparison should be 6 + 22 = 28 bytes vs 304 bytes, which is ~11x size inflation, much lower than ~51x.

I thought audiences, scopes and claims make the JWT flexible. So we can logout from some APIs but still the scopes to read some data can be inside another JWT. That scope can be mapped to an internal ID( a primary mobile device, for example ).
Instead of inventing a new security protocol in these cases we can issue multiple JWTs. Not too many. I think. But as far as the expiry time is concerned I am not sure. We can reduce the expiry time but in cases where we have a read token for a longer time we probably still need another challenge response mechanism. What could that be ?

Hi @spatial

I’m not 100% I’m following your question, but I’ll try to comment on a few things, please let me know if I’m missing something!

  1. Logout with JWT

You cannot actually “log out” if you are using JWTs for sessions. You would need to wait until the JWT expired. The alternative is to have some sort of lookup of “revoked” tokens, but at that point, it’s just an overly complicated session cookie.

  1. JWTs instead of another protocol

I think much of what you are describing is handled by OAuth 2.0 (and OIDC which uses JWTs).

  1. Another challenge-response mechanism for longer time tokens.

Take a look at OAuth 2 refresh tokens, I think this fills the role you are looking for.

Take a look at these videos to learn more:

I agree. This is how our implementation is. There is a token blacklist service.
I didn’t get how a refresh token is basically a challenge-response mechanism for tokens with long expiry times. Used to think it is just for refreshing.
But after reading some Okta articles I understand that the ‘refresh’ token is the only mechanism to handle that. But ‘refresh’ token can be used a fixed number of times. How do we know that the we can ‘refesh’ forever ?

Take a look at: Refresh access tokens | Okta Developer

When possible you can restrict refresh token usage to only be used once (this prevents certain types of replay attacks). When using a refresh token to get a new access token, the response will also contain a new refresh token. You would replace the old tokens with the new ones.

Does that help?

It does help. Thanks.

I think a simple solution to #2 is simply having a combination of both: Use JWTs to store user_id only (in subject claim), and then use a server-side in-mem cache (redis) to store any additional information.

In case of reported/detected weird activity, add JWT to blacklist of JWTs in server-side cache (until expiry, can clean this up later).

From what I understood, we could do the exact same with encrypted sessions, that end up being a lot less bloated?

Gonna read up more on those, not familiar with them, but I’m guessing there’s some session ID that’s saved client side instead?

I have been reading about this a lot and fail to understand the difference between session/sessionless implementation. Basically, I see 2 differences in both cases (Stateful vs Stateless using JWT)

  1. Stateless sessions cannot be invalidated.
  2. Stateless sessions can be huge.
  3. Implementation can be tough.

Regarding 1:
a) Stolen session vs Stolen session ID- I think it depends on how they got stolen. If there is a man in the middle then he can block the logout call and use the session too. On the other hand, if we keep JWT expiry small like 2 mins the attacker will only have a 2 min window to attack.
b) Cookie is compromised- Session ID is compromised. A part of JWT is compromised if kept in cookies.
c) Javascript Reading local storage: Session-Id is safe but JWT is compromised. Again splitting JWT will help.
d) Invalidating session on suspicious activity- How do you learn about suspicious activities? I believe since JWT claims are flexible and signed you can get the same information for validation as a session to match against the request. On any suspicion keep blocking the request. Although in the case of the session we can invalidate the session in the first incident which we cannot do in stateless implementation. Again If you have multiple servers storing sessions the replication of revoked status of the session may take time which could be similar to the JWT expiration time.

Regarding 2:
I won’t consider it a disadvantage as such. Horizontal scaling does bring instances of service closer to your geographical location. Also, HTTP2 and HTTP3 do have compression allowed. In the end, the industry is compromising on latency for horizontal scalability.

Regarding 3:
Implementation of sessions is also not easy. Highly sensitive session management can be prone to attacks that will cause frequent session invalidation. In some implementations, it can be a single point of failure. In some implementation databases used to store sessions can bring in some vulnerable components.

If one is already compromising on latency for horizontal scaling I do not practically see any problem with JWT apart from not being able to invalidate which is not a problem of JWT itself but of any stateless implementation. Provided best practices are followed as mentioned in RFC 8725: JSON Web Token Best Current Practices

Note: RFC 7009: OAuth 2.0 Token Revocation also talks about token revocation for OAUTH 2.0 but, it doesn’t look like a true stateless implementation.