A place I worked at recently insisted on JWTs despite being a monolith and calling the database on every API endpoint. I still don't get why.
A place I worked at recently insisted on JWTs despite being a monolith and calling the database on every API endpoint. I still don't get why.
Protecting your database from even being queried when the request is unauthorized reduces your attack surface enormously.
Encrypted session tokens with embedded expiry will serve the needs of 99% of applications/services.
I cited this as one example of that surface area that led to serious vulnerabilities. Most people don’t need multiple ways to encrypt their data, and certainly not a ‘no encryption’ option. Each added option adds more ways to mess things up.
Encrypted session cookies are a proven solution, widely used, and easy to implement yourself using only core libraries (http, crypto, JSON) without introducing security flaws IFF you are an experienced programmer and don’t deviate from the norm.
JWTs are complex. Too complex to implement yourself, so you need to introduce dependencies. Dependencies are usually huge, and each line of code introduces risk (even if that line is for a configuration you don’t use!). Using JWTs at all is far more risky, even if you are an experienced programmer.
If JWTs provides immense benefits over session tokens for your use case, that risk might be worth it. However, for most web apps, session tokens are good enough.
I guess DDOS attacks - if we're checking the session ID on every request then that's a potential attack if you just make up uuids and throw them at the server.
But JWT's themselves are an attack vector, surely? If there's any mistake in the encryption (or any vulnerability in the libraries used) then this is a door open very wide indeed.
The beauty of session IDs is the simplicity of it - very few moving parts, so very few opportunities for mischief. JWTs seem to be the opposite: lots of moving parts, lots of opportunities.
Comparing to session IDs, you have no way to know if an unique id is bogus or not. You have to check from a list, be it a cache or a database. This limits the scalability of the solution. I'm not an expert, but AFAIK JWT verifier can be stationed on the edge of the application network, and I have not checked this but I suspect they can even make a hardware solution for those kind of activity. That's definitely a big reduction of attack surface in terms of DDOS.
IMO JWT doesn't have that many moving parts. Encryption parameters are handled by libraries according to tested standards. The only real thing you need to do is to keep your private key safe.
So you have to check a signature, right? Maybe this is what the other commenter meant when mentioned encryption.
Session IDs don't have any of these problems. It does mean you have to do a database lookup on every request, and that can cause problems with scale, but if you have to hit the database anyway for other reasons then you have that problem anyway and have to solve it for those other reasons.
No need to reinvision the rest of the auth flow if you just want to add hashing to reduce DB load.
I've even seen a few cases of Stateful JWTs, where the JWT contained a session ID and everything else was in the DB. Of course, this approach manages to be both an overkill and a security and inferior to the just-use-an-HMAC approach at the same time.
I much prefer sticking with something simple and easy to comprehend, with fewer potential footguns, and revisiting the decision when we have enough traffic that it could be a concern. Just putting the tokens in Redis can go pretty damn far.
The reason you avoid the db query isn't about trying to keep the db from being overloaded, it's that you can handle the request far away from the DB without needing to wait for the latency of hitting the DB.
Which is also still a complete premature and unnecessary optimization for most people. And then someone realizes their use case requires revocation so they add a DB check anyways.
For myself I use short lived JWTs with a refresh token that I can invalidate. Yes, this means if there is a breach you have wait the refresh period but honestly I’ve never seen someone move faster than my refresh time to invalidate a session (on the rare cases I’ve ever seen it done professionally) so I’m not very concerned about the potential hole. It’s just not in my threat model and I have to say I question some people who say it’s in theirs.
I’m not saying there isn’t a use-case for immediate revocation but that a lot of people who talk about it don’t actually need it and/or have no good method for using it. Unless you have automated systems that revoke a token then I fail to see how 5 more minutes after a request worms its way through support is the end of the world. The damage is either already done or 5 more minutes isn’t going to be the end of the world (again, by this point it’s been going on for hours if not days/weeks).
Even if I wanted immediate revocations I’d just use an ID in a JWT and compare against a list since I still find the benefits from having role/tenant in the auth token very valuable. Then all I need is something like memcached or DynamoDB to hold a TTY’d ID for 5-ish minutes. But like I said, my threat model doesn’t require that.
Still, any modern database system could handle these lookups for all but the largest sites, so this smells like premature optimization for the vast majority of projects out there.
I understand that it's not inherently more secure or performant, but it is convenient.
I do get wanting to keep it simple but with sessions I don't know what is more simple once your project starts to grow. Rolling your own auth can be a minefield especially if you want to span it across more than one service and most of the off the shelf solutions will push you towards OAuth/OIDC any way at which point JWTs are some kind of de-facto standard. The good thing is that once one has gotten accustomed to OAuth verbiage you don't really have to think about it any more.
I resisted microservice mania when it emerged, and now I feel validated that the pendulum has swung back.
At my clients, the typical case is that the "product" consist of an assembly of services where some have been written and some bought so being able to span homogenous access controlls over them is a nice feature of JWTs/OIDC :-)
JWTs are really nice because you can validate things like roles and permissions just by validating the token signature. And there are much better ways to implement revocation lists than "calling the database on every API endpoint" (if that's what you were referring to). Since revocation lists are usually very small (depending on the nature of your app), it's often possible to just replicate them to in-memory data structures on your servers.