Hacker News new | ask | show | jobs
by jacopofar2 3379 days ago
The "none" algorithm set in header is a well known problem and, for example, nodejs most used library automatically uses asymmetric keys when one is given, ignoring the header (https://github.com/auth0/node-jsonwebtoken/blob/master/verif...)

As long as the problem is known to the developers and the key is specified, I think the biggest issue of JWT is the lack of session invalidation (that is, if you log out your already emitted tokens are still valid until their expiration), but it's a good tradeoff for not having server sessions.

6 comments

Session invalidation is possible though, by maintaining a (short) blacklist of tokens on the server. JSON Web Tokens can be given an ID (via the jti claim), and server-side these IDs can be matched against this blacklist. When you log out, you send a request to the service that your current token be blacklisted.

Because JSON Web Tokens are short-lived, the blacklist need only contain tokens valid for validity period plus a few seconds and remains very small (often empty).

If you use JWT to allow authorization on several server, then you do need to distribute this blacklist, so it is not a completely trivial solution. In the simplest scenario you might suffice with only maintaining a blacklist on the server that can refresh tokens (this means that when the token expires, a new one cannot be automatically acquired).

Yeah, and that's kinda sad because now you have to check a signature AND query a database!
s/database/in-memory-map/g should be fine - and suddenly it's pretty lightweight (subtracting service restarts and a highly available message bus of course :)
s/in-memory-map/cache-server/g if you happen to have a load balancer without sticky session.
In both cases there is a DB somewhere storing the list. The difference is that with the blacklist the server can keep an in-memory cache because it's so small. Sessions don't need to be invalidated atomically so the blacklist can be refreshed every couple of seconds.
Store it in a DB for persistence, but push it out to application memory. If for some reason you expect your blacklist to be very large (maybe, you have a massively popular API?), push a bloom filter of the blacklist instead of the actual list.

Now, you (probably) only absorb the DB hit on blacklisted tokens.

Two solutions:

1. As other posters pointed out. The blacklist is probably pretty small and can live in memory on your apps servers. If you have a distributed raft network or something to keep it in sync across nodes, even better.

2. You can avoid checking it against the DB unless the API call is sensitive (example: modifies data).

Yeah, of course you can do these things. I really meant to say, "there now exists server-side state for this" — I'm bothered by how existence of that state defeats the statelessness benefits of signature-based schemes, not the fact that I have to query a remote database.

Oh, and also: "only store a blacklist" does not work if you want to provide the "revoke this app you gave access to a while ago and now it's spamming" functionality like in most social networks.

Well you cache the blacklist and push updates, so it has no real performance cost. Just a tad more dev time
The none issue was highlighted by Tim McLean 2 years ago [0] and comes up in any trivial search about JWT. Surprised that anyone who chooses to use JWT is still getting caught by it as, as you say, any half decent library mitigates this.

For me, the log out / cross device session management issue seems to force a pattern of short expiry with self refreshing tokens. Commonly used devices feel always logged in, whereas uncommonly used devices end up needing a fresh log in each time.

0: https://www.chosenplaintext.ca/2015/03/31/jwt-algorithm-conf...

In terms of invalidation, I think a case-by-case basis is best, as it often is.

For example -

If some critical part of your app depends on a user's account or session being still valid, just do the check on that endpoint call (grab the sub/ID claim from the JWT and hit the DB, or similar).

The rest of the time - viewing stats/feed/whatever, admit that if the user had a valid token issued to them 5 minutes ago, it's probably OK to send them stats without having to check revocation (or whichever benefit of JWT you're exploiting).

Thing is, this at least gives you the /option/..

Exactly. The session invalidation has to happen using a session store or expiry header or something similar. In this regard JWT is not better than cookies.
> expiry header

JWT tokens have the expiration date embedded in the token. There is no way to force it to expire like you you can with cookies.

Although force is a strong word. Even with cookies if you tell the client to delete a cookie it doesn't mean it has to listen.

session invalidation is actually very easy to implement. Its important to think of it as a process instead of a builtin to the standard.

In most of our implementations we achieve this by differentiating between the session token and a request token. Requests that actually power the app use tokens that are very short lived. Request tokens are generated by the core auth server using the session token. A session can be invalidated at the core auth server which will then refuse to give request tokens to the bearer.

Session invalid is an issue with all Tokens.