...and beyond that, if an attacker can take out your invalidation server, which needn't be directly accessible to the public, you've already had a pretty serious security breech. I think the least of your problems would be the invalidation server.
Some security conversations get to the point where it's stringing together a highly unlikely chain of scenarios requiring multiple pivots, multiple concurrent failures (stochastic or otherwise), and a state sponsored actor in order to slap a 'do not use' recommendation on something.
If companies like Nike can run _Magento_ and educational sites still require Flash then I can use JWTs. Maybe...
TBH I don’t think the author of the article has expirenced the nightmare that is a hot session store at a large scale before, you end up with needing to troubleshoot IO latency issues with basically no tooling that can show you where the problem is and you’re up against the hardware limits and what ever black box your cloud provider has made. Where as with JWT everything happens in normal user space and can be reasonably reasoned about with a bit of complexity without razor thin latency deps on IO performance.
I'mnot sure that's entirely true. It's not hard to include an extra static token within the JWT which is delivered to the servers through an alternative method (pushed with production, or manually set), which when it changes forces a re-auth (either just a DB auth session because you've specifically set the conditions such that you only care if PW changed or some other flag set, or a full login procedure).
If the same static token is used for all users, all users will need to do this on the next request. If you subdivide your users by some metric (first x characters of username, some modulo of numeric ID) then you can invalidate some subset of the userbase JWT tokens instead.
You've now gained the ability to immediately invalidate a JWT tokens as long as you're okay with forcing extra computation on some larger set of completely valid ones at the same time.
Edit: Changed wording of last sentence to clarify you don't necessarily have to invalidate all sessions just by invalidating all JWT tokens, as outlined in further comments below.
So, should I think that you agree with the article?
The point is not to be stateless for its own sake, but to leverage less state in a way that's beneficial (e.g. not having to authenticate every request). If adding back a little bit of state in a different way mitigates the major downsides, that can still be useful if it doesn't entirely negate the benefits already gained.
The article has a perfectly nice point, that nearly every site does not need stateless sessions. Keeping them on the database is perfectly fine.
The article also has a point that there it is impossible to revoke certificates without shared state.
People here have a point in that it is possible to optimize that shared state as much as it is a performance non-problem.
Other people here have a point in that this is a hard problem that isn't solved by default, so you either do some low level work creating your revocation list from basically the ground up, or you'll get something that is worse than a database.
Yet other people have a point in that it's hard to set your PKI correctly and if you don't want to bother with that, you are better with a standard database implementation.
All of those are true.
Of course, this gets to the point of the article -- if you're loading data serverside, that's not reeeaaallly the intended multi-party-claim-exchange use case of JWT's....
> every time someone changes their password—which should certainly invalidate their old tokens
Using a variation of above, you can just reverify the JWT every X minutes, and know that if you change a password for normal reasons all JWT tokens will be invalidated within X minutes.
It's a trade off. If you can live with what you're trading (or at least live with it after mitigations are put in place), it might be worth it.
The reason for using JWT is that the UI and backend consume the same session object seamlessly. Before what we got in our PHP session and what state we shared with the UI were manually kept in sync through a API request.
what fields are in this session object besides an identifier?
But, what are you getting for this added complexity--above and beyond what "traditional" user session management provides?
Of course, if the DB concern is one of performance, then there's also a performance trade-off with JWT since the (sometimes large) token is constantly transferred over the wire and compute is used to decrypt it. That may be faster than a DB-round trip, but it's not free. Of course, "old-school" session-management keeps the auth state in memory server-side, which can be the fastest--though comes with a memory hit. But, constantly processing the token in-memory also assigns a memory penalty (even if slightly more transient).
In any case, I think it's like a lot of popular tech that evokes strong reactions on both sides: The success of the tech causes it to be overused, invariably in use-cases for which it isn't as well-suited or was not originally designed.
In this case, the important need for session expiration alone makes me question hard whether JWT is a good fit for session management in a lot of user-facing apps. In some cases it will be, but in others, there are probably better solutions.
There are cases where JWT can make a lot of sense. For example, a high capacity API where you don't necessarily want to check auth for every request, especially if you can rely on some sort of caching infrastructure. In that case it may make sense to use JWT, re-check the authentication on some set schedule (whether once an hour or day), and save yourself what might be a few thousand authentications a minute.
With the advent of SPAs and RESTful designs, it was probably tempting to say, "hey, let's allow our user-facing client apps to hit the API directly and use the same JWT token scheme for auth there". So, whereas it was generally a good scheme for APIs, it became a YMMV thing once it diverged into the client.
I can definitely see the appeal of JWT tokens for infrastructures with many decentralized services and a very high request load, even when taking into account the added complexity due to invalidation issues.
The real reason for these articles I think is that some developers had a very traumatic experience with a poorly implemented JWT authentication system once in their career and now nothing will change their minds about JWTs.
Maybe JWTs don't make as much sense for HTTP but for WebSockets they are absolutely crucial. With WebSockets for example, you can push the JWTs to the clients so you can make them with very short expiry: e.g. even 10 minute expiry is possible... Issuing/renewing JWTs with very short expiry helps avoid the need to explicitly invalidate them... If used correctly, JWTs can take a lot of load off from the database.
I could go on for a long time about the benefits of JWTs with WebSockets but to summarize the best aspect for me is that you can push them to the client with short expiry and also that you can automatically renew/re-issue them on an interval while the connection is alive... It also works well for dealing with socket reconnects and saves database lookups.
Someone else called it FUD, now you're calling it propaganda. I'm seeing a pattern.
> This is not the first article of it's kind; they just keep popping up over and over on HN with the same poor arguments. I usually post long explanations as to why these points are invalid but I'm tired of arguing with these people.
...they said, commenting on an Internet board where discussions (a.k.a. arguments) often unfold with these people.
Why are these arguments poor? In what sense does "JWT instead of server-side storage" make a better engineering decision?
> The real reason for these articles I think is that some developers had a very traumatic experience with poorly implemented JWT authentication system once in their life and now nothing will convince them otherwise.
Your ad hominem straw man argument notwithstanding, there are two orthogonal arguments that JWT defenders seem to conflate:
1. Don't misuse JWT in places it wasn't designed for. <- You are here
2. JWT is an error-prone cryptographic design and should be replaced with a better standard.
You can agree with one without agreeing with the other.> Maybe JWTs don't make as. much sense for HTTP but for WebSockets they are absolitely crucial.
Would PASETO be a better fit than JWT for your envisioned WebSockets use case?
Could you link to one of your long explanations ? I'm interested in a rebutal but understand it's annoying to repeat yourself.
Unless that's in regards to checking whether an auth has been blacklisted, which isn't something jwt solves, as far as I can tell.
JWTs can be stored in cookies. So I don't understand why you say this.
>A lot of people mistakenly try to compare "cookies vs. JWT". This comparison makes no sense at all, and it's comparing apples to oranges - cookies are a storage mechanism, whereas JWT tokens are cryptographically signed tokens.
There's no such thing as "JWT exclusively". The JWT is just a chunk of data; it has to be stored somewhere, either in a cookie or in local storage.
Whether we can take this as evidence of a big following in actual production implementations I’m not so sure, but if forced to guess I suspect they are exceedingly rare (compared to stateful systems) outside of programmer side projects.
I’ve rarely ever seen stateless auth used on anything serious, as others have noted many times before the issues with invalidating previously issued tokens often removes the “stateless” part, after which you are just as well using more traditional mechanisms.
It's trivial to store a token version on your user object and invalidate any token that has the wrong version.
They usually don’t understand the issues around the things they want to build, they do it because it’s cool and new, and don’t stop to ask or research anything.
On a serious note: JWTs have use cases, but your company is probably not the one. Nice thing about JWT is that there is no need to query the DB every time a request hits a service. JWTs for session should be short lived and refreshed often. In case you need to invalidate it, you invalidate refresh token and window for attack becomes short. It's not perfect but it's certainly not useless and just the hype.
If you're doing high-ops and your goal is to reduce authorization calls the TTL acts effectively as a cache that decouples API ops and authorization ops.
The compromise being that you can only rotate as fast as the TTL.
That is of course unless you want to implement revocation-list capabilities. But if your revocation-list server goes down you're back to the TTL limitation.
You don't need to have Reddit-scale traffic to have to consider this, but when you deal with a lot of backend systems it can reduce complexity a lot.
That said, I don't think a lot of people really take this into consideration when they use a stateless JWTs, or the computation required to issue really short lived tokens.
Aaaand now I have to check my fire extinguisher.
I think the right answer is that you need to develop some type of threat models, review them periodically, and then decide what's right. Just because you can't think of a problem with the approach now doesn't mean it won't happen later, or be a component of a larger attack. The "given time frame" above may be more than enough for certain attacks you can identify in initial threat modeling.
From the long-term operational perspective, it may also be preferable for the application owner to have immediate invalidation. This might be inconvenient to the development team just trying to get their work done now, but is it a good idea to ignore this requirement? For various reasons, dev teams tend to push on only for there to be problems later.
Instead of `SELECT isValid from tokens where token="<TOKEN>"`, why not `SELECT user from sessions where session="<COOKIE-SESSION>"`?
Changing the static token causes all JWT token verifications to fail (since the token is stored in the JWT),so requires each user re-auth on the next request. It's a manual fail-safe to invalidate stored sessions.
This, of course can spike load considerably in the period immediately after, so if that's a problem, you can always use multiple tokens based on some criteria, such as one matching the first letter of the username, or the user id mod some value, so split your user into N chunks that each have their own invalidation token, so you only have to invalidate the sessions of 1/N users.
So if a single user's token gets compromised, you force ALL users to log in again?
All the token does is force some extra level of scrutiny. It doesn't mean the JWT has to be invalidated,but it does give you the ability to selectively invalidate specific ones for the cost of extra DB load once for all affected users.
Anyway, I'm still not convinced because when it comes to security I feel like it's a better idea to walk the well-trodden path, rather than invent new and exciting mechanisms. JWTs are useful for other stuff, but using them for security requires increasing back-end complexity quite a bit.
If you check against a single constant in the backend, are you invalidating all JWTs for all users whenever you want to invalidate only one? Even if you keep a blacklist of invalidated JWTs, how do you keep it up to date? How do you distribute it? What happens if the blacklist becomes unavailable for whatever reasons?
User gets banned or privileges revoked? Fine, revoke the master token on the authentication server and after 5 minutes at the latest, the user is locked out as he cannot get new valid short-lived tokens.
Sessions aren't difficult - as long as you stick to one language and environment (e.g. PHP). But as soon as you throw in for example Java to communicate with a SAP backend (as an online-shop system might need to), nodejs for a "support/sales chat" system and whatever, a JWT scheme is easier to implement.
This is exactly the ability which JWT lacks.
You can, of course, combine a session ID (checked against a DB) with a short-lived token (stateless, with expiration time encoded). This will allow to only check the session against a DB if, say, more than 30 seconds has passed since the previous check. It helps with spiky traffic, and still allows to invalidate a session soon enough for practical reasons.
That might be fine for a lot of use cases. But anything that can steal money using that token and 5 minutes is a very long time indeed.
But anything that can steal money using that token and 5 minutes is a very long time indeed.
Then implement a check for the token in destructive operations. Should keep the performance impact pretty low.I believe people are downvoting you because you may have overlooked the processing that must happen on the server to encode the token in the first place.
The SLJWT does seem useful, but 1h seems too much; if I fear someone might be (for example) accessing my email account using a stolen or hijacked device, they can do plenty of damage in that time. Do you make it clear to the user that their request for a remote sign out might take that long to be applied?
It doesn't have to hit the DB, it could hit memcache or similar caching layer. It also means we could separate this table out from our main DB. We have done neither because it hasn't been an issue (performance-wise) yet.
> Why not just store that UUID by itself?
We could do that, our thought process was that we wanted everything to be a JWT and we liked the ability to, given a JWT, know who it was issued to, when it was issues, and when it expires, all without going to the DB.
> The SLJWT does seem useful, but 1h seems too much; if I fear someone might be (for example) accessing my email account using a stolen or hijacked device, they can do plenty of damage in that time. Do you make it clear to the user that their request for a remote sign out might take that long to be applied?
Totally agree on 1hr being too long and that is why we are planning on lowering it. We have technical reasons (legacy apps that we are trying to get updated) that will not work with lower than 1 hour (I won't bore people here with the details and there are workarounds but we haven't implemented them yet)
Edit: One more point, we also like how JWT's are language agnostic so that we can jump between languages very easily if needed and still use the exact same concepts. Sharing sessions between, say, Java and PHP is not something that is simple/fun/easy to do. JWT's are also easier for us to reason around with, maybe it's just me or my team but I see them as more finite than sessions.
Everything from there goes to "Congratulations! You've just re-invented sessions (only with a less battle-tested implementation) and gained nothing in the process"
(Sarcasm is from the author's post, not mine!)
Also, in my case, "your blacklisting/revocation server goes down" means the whole application is down anyway, so that's kind of a moot point.
You may disagree, and there are valid reasons to avoid JWT. I'm just saying that under the right circumstances, it can be useful.
[1] The author claims that JWT isn't any easier, but then later says things like "Expiration can be implemented server-side just as well, and many implementations do". That's true, but it is something extra you have to implement yourself, i.e., not easier (for that feature at least).
So just use a session library with expiration implemented, don’t take one without and add it on yourself.
For some of my particular use cases[2], I happen to prefer the JWT approach (despite all the points given in the article, and after auditing the libraries used). For others, I definitely prefer traditional session tokens.
[1] Just because it’s been implemented by someone else, the complexity of that code still falls on you - especially so with something as critical as authentication. It’s not automatically “easy” because someone else implemented it; I’ve seen some pretty terrible security issues in both JWT libraries (e.g., insecure defaults) and framework-provided session management libraries.
[2] Guess what? If you have tens or hundreds of millions of users, managing sessions and expiration can be a bit of a pain. Example: to keep things performant, you'll want to periodically clean up your session store (rather than just invalidating at lookup). Congratulations - now you have a background worker and the complexities of dealing with that at scale. Point is, even with nice framework-provided libraries, managing session tokens on the server can add considerable complexity.
The author does sound negative, but doesn't claim JWTs are categorically bad; he actually mentions cases where JWTs are useful: when they are used as single-use tokens. The author claims JWTs as sessions are too problematic to be useful.
> I’ve seen some pretty terrible security issues in both JWT and framework-provided session management libraries.
It seems to me the author is arguing JWTs (used as sessions) are more error prone and less battle tested than traditional session management. So if you've seen terrible security issues...
As for session cookies: like the author says, cookies are a storage medium and orthogonal to the issue. You can have all the problems of JWTs in addition to all the problems of cookies.
If you’re registering a dedicated client id and secret for each web client, they you’re doing something wrong. If you’re doing this and then also using JWT, then you’re doing something really bizarre, and still wrong.
Id/secret pairs can make lots of sense for integrating partner services with your API. They make no sense for web clients, where you should be authing a user and not a client.
Please do go find your tribe of condescending devs with fragile egos.
How can you invalidate my server now?
How you invalidate either is the same, whether its a database lookup or something else. Article and most of the comments are wholly inaccurate.
Otherwise, they could just use a long random string and maybe HMAC it and call it a day.
There are some common usage conventions but the limits described in this article are completely arbitrary.
The title is "Stop using JWT for sessions" not "Stop using JWT".
The arguments against JWT themselves are wholly separate: https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-ba...
If you want to say that client sessions should not be 100% on the client side and should be split between client/server then just say that. There are pros/cons but at least that is a clear argument instead of misleading about JWTs.
I don't even see why you believe this to be the case. The article (and the follow-up) are both pretty clear about what the actual issue is.
Did you read them or just react to the headline?
I think you can also see from my other comments that I know what I'm talking about and my issue is the fact that * instead of discussing client/stateless vs client/server shared state*, this article spreads more misleading information about JWTs and sessions.
Whether you manage state 100% on client or share it between client and server is an entirely separate topic.
Sure you can. Just have a 'date_password_last_changed' field on your user model, and compare that with the issued_at date on the jwt. Since your user model presumably gets retrieved for each authenticated request anyway, there are zero extra database lookups. Similarly, if there is a global compromise then just rotate the secret key use used to signed the JWT.
If someone is changing their password because it has potentially been compromised, what is a scenario where you would not want to invalidate all of their existing tokens?
This is why it usually takes some websites 30-60min to completely revoke your password. This may be a concern for highly secure apps, but for most apps I think this will suffice.
I simply offered a reasonable solution to revoke JWT tokens. Would I use them in a project? Probably not. Even so, if an attacker has compromised someone's session, it's probably too late.
> You cannot invalidate individual JWT tokens 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.