7 ms·
> Necessary qualifier: for browser-based user sessions. > Plenty of good uses for JWTs for service-to-service communication. This is the sensible conclusion r
by RagingCactus 4mo ago
> Necessary qualifier: for browser-based user sessions.
> Plenty of good uses for JWTs for service-to-service communication.
This is the sensible conclusion right there. I agree JWTs are the wrong tool for the use case of user sessions in the browser.
To give some more arguments:
All the signature and encryption stuff in JWTs is complex. While common JWT libraries have now mostly got their stuff together, this has not always been the case. There were plenty of libraries accepting the "none" algorithm [1] or allowing attackers to forge tokens by using a public key as a shared secret [2]. This is the direct result of the complexity criticized in the linked blog post.
JWTs also cannot do some stuff you want for user sessions. You can't invalidate them without keeping a revocation list somewhere. But if you have to check an identifier for revocation on every request you could just use an opaque session ID and look that up on every request instead! Sure, you can use short-lived tokens and refresh them all the time, but why bother with that for a typical application that has to keep some state anyway?
All that being said, I wholeheartedly agree that there are use cases in distributed systems and machine-to-machine communication where signed tokens can be useful. Just please don't confuse the two cases.
[1] https://nvd.nist.gov/vuln/detail/cve-2022-23540 https://nvd.nist.gov/vuln/detail/cve-2022-23540
[2] https://nvd.nist.gov/vuln/detail/CVE-2024-54150 https://nvd.nist.gov/vuln/detail/CVE-2024-54150 (just a random example from googling, I don't know what library made this one infamous)
- nine_k 4mo ago> if you have to check an identifier for revocation on every request you could just use an opaque session ID and look that up on every request instead! One reason could be the size. A revocation list only needs to keep session IDs of recently logged-out sessions, for which the token's TTL hasn't yet expired. It may be a much smaller list than a list of every active session. Also, a JWT (or a Macaroon, etc) can store a large amount of details about the session in a cryptographically secure, unforgeable way. This rids you of the necessity to store all that in your active session database, again cutting the size.
- agwa 4mo agoAs someone who operates a PostgreSQL database containing 27 billion SSL certificates, each 1-2kb each, with a bunch of secondary indexes that get inserted in random order, I find it pretty incredible that people see the need to optimize their session database. At what scale does the size of the session database actually matter? Those stateless tokens may be "unforgeable", but they are replayable, and if you're not mindful of that you can have security vulnerabilities.
- hparadiz 4mo agoYou should do some basic optimizations. Fixed length table and indexes on the unique string for fast lookups. I also like to do a rolling delete for old sessions after 30 days unless mobile session that is logged in. Those get to live forever.
- agwa 4mo agoFair enough, but those optimizations are basically free. People think stateless tokens are free but they really are not.
- hparadiz 4mo agoThe cost of the stateless token is basically the CPU usage for signing the message and checking the signature with the public key on the client. Example: Google Compute Instance asks metadata server for OIDC token (which is a JWT). The metadata server respond with the token that basically says "here's the machine service account, here's the machines ID, this token is proof that I am service account abc123 and it's valid for 20 seconds". This is one of the most common uses of JWTs in enterprise. You don't store them. They actually are free. Lots of web devs get tricked into using them as primary session tokens and it's a huge anti pattern. I see it all the time and people get aggressive about it.
- agwa 4mo agoThe cost is the vigilance required to use them safely. It's not just compute/storage costs.
- saganus 4mo agoI am still waiting for Macaroons to be used widely. I think they are a fantastic invention. It seems they were not of very much use in the past, but with the agentic-everything now, I see this as a great way of delegating permissions to subagents, third-party agents, etc. Working on something along these lines but unfortunately I cannot dedicate as much time as I'd like. Still, if anyone is reading, give Macaroons a try!
- jiveturkey 4mo agoJWTs can do that (delegate) and such capability is already well defined.
- saganus 4mo agoMaybe I stated it wrong. Macaroons have the ability to attenuate the restrictions _without_ contacting the auth server, which makes it IMO fit for restricting and attenuating as much as you want, without much cost. If I need a roundtrip to the auth server to attenuate, I am not necessarily going to do it as often.
- tptacek 4mo agoMost token formats delegate. Macaroons support attenuation, confinement, and embedded third-party claims, none of which are JWT capabilities.
- malfist 4mo agoI like the raspberry ones. Or lemon is also good
- smallerize 4mo agoAre you thinking of macarons? Macaroons are coconut.
- tptacek 4mo agoWe have what I believe to be one of the world's largest deployments of Macaroons. They're a mixed bag, though I think they're a lot more interesting in a world where agents do most of the fiddly work. https://fly.io/blog/operationalizing-macaroons/ https://fly.io/blog/operationalizing-macaroons/
- jongjong 4mo agoAnother point is that managing session data on the server-side is a pain... If your app server goes down, stale session data would be left behind in your session store; it can easily become orphaned... So you need to set an expiry on it to ensure that it will be cleaned up no matter what... But you need to keep extending the expiry while the user is still online. God forbid you create the session data before you set the expiry on it and the operation that sets the expiry fails (e.g. server crashes at the precise moment or some error occurs which causes it to be skipped)... In practice, it's hard to avoid stale/orphaned session data. And yes, you need to store and manage more data and your session store is an additional Single Point of Failure... With JWT the revocation list is an optional... Your system can keep running without it; it just won't be able to ban users. It's a cleaner separation of concerns without SPoF. JWTs have so many benefits over session IDs, I could write a book about all the benefits. Sure, there are some tradeoffs but the negatives are typically pretty minor or hand-wavy.
- angoragoats 4mo ago> Another point is that managing session data on the server-side is a pain... If your app server goes down, stale session data would be left behind in your session store; it can easily become orphaned... So you need to set an expiry on it to ensure that it will be cleaned up no matter what... But you need to keep extending the expiry while the user is still online. God forbid you create the session data before you set the expiry on it and the operation that sets the expiry fails (e.g. server crashes at the precise moment or some error occurs which causes it to be skipped)... In practice, it's hard to avoid stale/orphaned session data. Tell me you haven’t been writing web apps for a long time without telling me. There are many ways to solve the problems you describe here, which have been put into widespread use by web frameworks serving literally billions of users stretching back decades. Why reinvent the wheel with something that wasn’t designed to be a long-lived session store in the first place? I think the Rails default is quite clever (the entire session lives in a cookie on the client), which gets around most of the problems you specify. But there are many other approaches if that doesn’t work for your use case.
- jongjong 4mo ago
- hparadiz 4mo agoIf you don't understand conceptually how to verify a signature with a public key the very first thing you should do is get that working and then work from there. It's completely unacceptable to ship without this.
- robertlagrant 4mo ago> While common JWT libraries have now mostly got their stuff together, this has not always been the case. There were plenty of libraries accepting the "none" algorithm [1] or allowing attackers to forge tokens by using a public key as a shared secret [2]. This is the direct result of the complexity criticized in the linked blog post. I'm a bit surprised at this. These are extremely simple to solve - the first time I ever did a JWT-reading implementation I specified the right defaults, which are very simple, even for a mid-level backend person I would say, and they haven't needed changing in 8 years or whatever it's been. It really isn't very complex.
- agwa 4mo agoYou would think so, but even an authentication company screwed it up: https://cybercx.co.nz/blog/json-web-token-validation-bypass-in-auth0-authentication-api/ https://cybercx.co.nz/blog/json-web-token-validation-bypass-...
- y2244 4mo agoWow lol
- userbinator 4mo agoClearly trying to be too general. I wrote a tiny JWT validator before that only allows a very small subset of algorithms because I wasn't expecting the JWTs it would handle to have anything else, and obviously not "none".
- jongjong 4mo agoThe people who are upset about JWTs probably got burned by trying to use them in a weird way. Some people try to store sensitive data inside JWTs... WTF, the idea would never have entered my mind! Sensitive data should stay on your server. Encrypted or not! JWTs are supposed to be signed, not encrypted! You shouldn't even think to put sensitive info in there. Also, WTF is wrong with people who accepted algorithm "none." Most of these people who tried to tweak the defaults had no idea what they were doing in the first place.
- LgWoodenBadger 4mo agoCome on, it’s not like the two are even within the same magnitude or three “But if you have to check an identifier for revocation on every request you could just use an opaque session ID and look that up on every request instead!”
- chuckadams 4mo agoA revocation list defeats the purpose of JWTs. If you find yourself needing one, JWTs were probably the wrong choice to begin with.
- jongjong 4mo agoNot really. Being able to verify a user's signed identity immediately without having to do any database or memory store lookup first is valuable in itself. With session IDs, anyone can spam/DDoS your system much more easily; they don't even need to be authenticated to waste your computing resources as they can send plausible-looking session IDs and your system will waste a ton of resources querying your session store or database to figure out that the session doesn't exist... It adds a ton of latency and wastes CPU cycles across multiple systems. Also stateful systems like Redis are harder and more expensive to scale than stateless systems like application servers. Not to mention that they may be depended upon by other parts of the application so hitting those too hard can be more disruptive. And that's kind of best-case. Some people use a database to store their session data... At least with a revocation list with JWT. If the JWT says that the user is user1234, then you know that this is a real, previously logged-in user, they have an account at stake, you can afford to spend a bit of time/resources to check them against your revocation list... And if they are on that list, you can ban their ass and they're done! They'd have to create a new account of they try to spam you again. They can't spam without a valid JWT and they can only get that by authenticating with your server first. Sure with JWT, some computation is spent on verifying the signature but it's very cheap using the default algorithm and only touches one system... And you only need to call another system once you know that the user is valid. JWT is much more robust for DDoS prevention because of this. But yeah, you don't necessarily need a revocation list. You can use short JWT expiries with frequent token refreshes. Revocation list is good if you need immediate ban.
- hasley 4mo agoCan't a system be DDoS'ed with wrongly signed JWTs as well? Is signature checking (much) cheaper than finding an opaque session ID in a database?
- figassis 4mo agoStateless JWT revocation: https://blog.nellcorp.com/new-aproach-to-jwt-revocation/ https://blog.nellcorp.com/new-aproach-to-jwt-revocation/
- dchest 4mo agoWTF: > Each user has a secret: Stored securely in the database. > Stateless Validation: The core validation remains stateless. We only need to consult the database for the user's secret, which we'd likely do anyway for authorization checks. Is "stateless" the same as "serverless" now? Is author's brain stateless?
- figassis 4mo agoA JWT is usually signed, with a secret you keep in your app. The statelessness of JWT is that it contains all the information you need to verify it. You do not need to ask a db if the token is there and valid. Storing a user's secret, the same way you store your applications secret does not make it more or less stateless. In since you now have 2 layers of protection, you don't actually need to verify agains a user's secret immediately, you simply need to check that the token is valid using the app secret. The subset of valid tokens that you need to check is much smaller than the universe of all the unexpired tokens your application has issued. If you have a security incident and need to revoke tokens for only a subset of your users, now you don't need to rotate your app secret and invalidate every single token and break every single session. You can simply log those users out. Is author's brain stateless -- my bad, I thought this was not reddit
- dualbus 4mo ago> [...] you don't actually need to verify agains a user's secret immediately, you simply need to check that the token is valid using the app secret. The subset of valid tokens that you need to check is much smaller than the universe of all the unexpired tokens your application has issued. What you are describing here is different than what is described in the blog post that you linked to. Please look at the definition of the function 'validateToken'. In particular, notice how 'getUser' function (which the author notes issues a DB query) is called for every JWT with a valid signature! EDIT: I failed to realize that you are the author of the blog post. Still my point stands, in that your description doesn't match what the code does.
- 0x696C6961 4mo agoThe design I've landed over the years is to use both. The cookie is a session token and that's where you handle refresh tokens. Then there's an endpoint where you can mint a short-lived tenant-sepecific JWT. This holds the scopes & tenant id. The session token only lets you access the web assets & mint JWT tokens.
- locknitpicker 4mo ago> All the signature and encryption stuff in JWTs is complex. While common JWT libraries have now mostly got their stuff together, this has not always been the case. This is a red herring. Applied cryptography was never considered an easy subject in software engineering circles. Neither was algorithms and data structures. Yet, it's still a basic tool, and developers are still expected to understand things such as why some maps allow reads in constant time while others require log time. Some libraries being buggy never was an argument against using libraries. And do you expect your single-purpose code not to be? I think we are seeing in this thread a knee-jerk reaction against perceived complexity. Yet, if you sit down and compare JWTs with alternatives and list all features along with pros and cons, you'd be hard-pressed to even try to put together a case for JWTs being bad.
- RagingCactus 4mo ago> Some libraries being buggy never was an argument against using libraries. And do you expect your single-purpose code not to be? Of course you should use battle-tested and well-maintained libraries for the really hard stuff such as cryptography primitives. However, that is not the point I was trying to make. My points here are: 1) If you can get away with not using cryptography for something, you probably should. Your web framework already supports session cookies. Even if it doesn't, it's very hard to mess up opaque tokens from SecureRandom or /dev/urandom and a corresponding database lookup. 2) If you actually need the things JWTs can do, the standard is still needlessly complex and easy to mess up in ways that are not inherent to the problems JWTs are trying to solve. I'm not saying this means you should roll your own solution (again, I agree that there is value in well-tested libraries), I'm further strengthening point 1) with this. Don't use JWTs if you don't need to
- unscaled 4mo agoI think both you and GP are somewhat misrepresenting the OP is saying. OP's argument is three-fold: 1. JWTs are not a good fit for a session token (although there are several RFCs that are trying to shoe-horn JWTs into this use). > TLDR: JWTs should not be used for keeping your user logged in. They are not designed for this purpose, they are not secure, and there is a much better tool which is designed for it: regular cookie sessions. 2. JWTs have other "valid" use cases that only need a very short-lived token (e.g. a transit token or a request signature) and don't need to care about user authentication, revocation, XSS etc. 3. But JWT should not be used even for the "valid" use cases, since you have better (read: less outrageously insecure) alternatives nowadays. > Also note that "valid" usecases for JWTs at the end of the video can also be easily handled by other, better, and more secure tools. Specifically, PASETO You've noted these issues yourself. There are many common vulnerabilities with JWT: alg=none, algorithm confusion and weak key brute-forcing, mandating weaker algorithms like RSA and ECDSA while making the best, fastest and easiest to implement algorithms like EdDSA "optional". There are also other design deficiencies that JWT makes by trying to be a generic cryptographic envelope format rather than a token format: e.g. expiration can be omitted and this feature that caused some libraries to not verify expiration by default or have a different (and confusing) set of token parsing methods that do not enforce the expiry. PASETO is a better design that is secure against all of these issues. Sure, there are a few minor qualms I can find with PASETO (e.g. no mandatory key ID and no support for non-JSON payloads), but it's unlikely to face the same avalanche of CVEs we got with JWT libraries.
- jappgar 4mo agoMost requests are reads and letting someone use an invalidated session for reads for 30 seconds on a shortlived token isn't the end of the world, especially considering that the exact invalidation timing and its propagation is already somewhat arbitrary. For rarer privileged actions you can check a token revocation list.