Hacker News new | ask | show | jobs
by vova_hn2 2 hours ago
One of the articles that TFA links to [0] contains the following paragraphs:

> And there are more security problems. Unlike sessions - which can be invalidated by the server whenever it feels like it - individual stateless JWT tokens cannot be invalidated. By design, they will be valid until they expire, no matter what happens. This means that you cannot, for example, invalidate the session of an attacker after detecting a compromise. You also cannot invalidate old sessions when a user changes their password.

> You are essentially powerless, and cannot 'kill' a session without building complex (and stateful!) infrastructure to explicitly detect and reject them, defeating the entire point of using stateless JWT tokens to begin with.

I'm not sure that this is entirely true. Typically, the total number of non-expired issued tokens is much higher than the number of invalidated unexpired tokens. Therefore, if you store only invalidated tokens and delete them when they get expired, you can significantly reduce the amount of required storage and the cost of lookup.

Although, in any real application the performance gains will be minuscule (compared to the cost of, you know, everything else. Auth is just a small part) and probably not worth the extra complexity.

[0] "Stop using JWT for sessions" - http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-fo...

2 comments

>> You are essentially powerless, and cannot 'kill' a session without building complex (and stateful!) infrastructure to explicitly detect and reject them, defeating the entire point of using stateless JWT tokens to begin with.

> I'm not sure that this is entirely true.

You can be sure it is not true, because it is utter BS. JWTs have an "iat" timestamp field (issued at) and in the described case that an attacker has a leaked token, your validation logic simply should refuse any token with iat < $NOW for that identity.

I have JWTs implemented for a site and in my case, users cannot individually revoke tokens - but they have a "Signout from all devices" option. That will basically just set a field "minimum_issued_at" to $NOW in the database for their user, and any tokens will always be validated against the minimum iat timestamp. That is a good compromise in security and simplicity.

Revocation lists have their purpose, though, in systems with heightened security requirements.

> your validation logic simply should refuse any token before $NOW.

Well, this approach throws out a lot of babies with the bathwater. You invalidate tons of legitimate tokens along with the one that you wanted to invalidate and get a thundering herd [0] of clients wishing to re-authenticate.

This is probably not good in case of a really high load.

And if you don't have a really high load, then there is no good reason not to have a stateful session storage.

[0] https://en.wikipedia.org/wiki/Thundering_herd_problem

I edited my comment after I posted it to clearify you do this on a per-identity basis. I.e. every user/identity has a minimum_issued_at field. A user can "sign out from all devices", and that will simply update minimum_issued_at with $NOW.

You are not throwing out a lot of babies with the bathwater if you would do it in a case of a known attack. You would invalidate ALL tokens of a user, which is a sane default especially since usually you wouldn't be able to rule out what other tokens were compromised. And yes, if it later turned out ALL your users and all their token were possibly compromised because you had some kind of security flaw, setting a global minimum_issued_at is exactly what you would do after you fixed the flaw. And yes, that means all your users must reauthenticate.

Thanks for the correction, I didn't think about this approach and it sounds like it should work.

The only comment that I have that if you are already querying users table (or collection in case of NoSQL or whatever), you might as well have a sessions table/collection in the same database/storage and query them together. It seems that difference is not that big.

The purported advantage of stateless sessions is that you can check the auth without querying the main db/storage (maybe only querying a smaller/faster axillary storage).

Think a small (as in client base), but distributed system - i.e. Asia/EU/US locations of a webshop. You can easily replicate/cache your products from a central server, and reuse the cache from the localized ones. But each and every web request would have to be authenticated against a central db somewhere around the world. It is just easier if each node can just validate the JWT themselves by using crypto. All they need to do is maintain a revocation list locally. Now, your revocation list is append-only, can be publicy available and never going to be more than a couple MB. Very easy to replicate/cache this. I can't say the same for a session database.
> your validation logic simply should refuse any token with iat < $NOW for that identity.

makes no sense

... ok now it does :) your now is not now, but a stored value

It really doesn’t seem very hard to have a small invalidation list. Just a redis cache or a simple broadcaster, etc.

Does anyone have an example of how they built a JWT revocation service?

See my sibling comment about the "signout from all devices / iat" pattern. This is only a few lines of code.

If you want to be more fancy and fast, you can use bloom filters to check if a token is in a revocation list.