Hacker News new | past | comments | ask | show | jobs | submit login

Off topic: I'm always confused by the passion to use JWTs where normal session uuid's would do just as well.

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.




JWTs let you reject unauthorized requests on their face, without having to even make a database call.

Protecting your database from even being queried when the request is unauthorized reduces your attack surface enormously.


So sign your UUIDs and combine them into “$UUID:$HASH” strings for the same benefit. Or a more structured JWT-like payload that still verifies auth against the DB (as opposed to carrying authorization within the token).

No need to reinvision the rest of the auth flow if you just want to add hashing to reduce DB load.


so ... recreate jwt?


If you're worried about DDoS (and also timing attacks, if you've got a B-Tree index), you can just append an HMAC to your random session ID.

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 don't get why that's a reduction in attack surface?

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.


JWT doesn't encrypt (by default) the payload. The header & payload is passed through base64 and appended with a hash to produce a JWT. JWT verifying doesn't require making API call, and is essentially a hash check. Any verifier with a public verification key can determine if the JWT is bogus by a quick hash check and reject the request right away.

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.


> Any verifier with a public verification key can determine if the JWT is bogus

So you have to check a signature, right? Maybe this is what the other commenter meant when mentioned encryption.


Yeah. The mechanics of JWTs are complex, involving maths that is easy to get wrong. Hopefully the implementation is based on a standard library and the author didn't roll their own crypto. Hopefully the library hasn't got any known vulnerabilities. Hopefully the implementation hasn't got any problems. Because the JWT actually contains sensitive information that is useful to an attacker, and if they can forge their own JWTs then the system is wide open; all the attacker needs to do is work out the username of an admin (or worse, the JWT itself tells the system if the user is an admin).

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.


So does every Rails and Django encrypted session token.


Sure. Just pushing back on the ‘you’re going to hit the database anyway’ argument. Having some kind of mechanism for eliminating obviously bogus requests is a good idea. Some sort of signed, expiring token is probably a good idea for that. Rolling your own expiry/signing mechanism is probably a bad idea. Correctly validated JWTs are an option (among many) for solving that problem.


How do you revoke an encrypted session token that doesn't need to hit the database to see if the request is authorized?


Include an expiry time in the encrypted part. The encryption, and the fact the token decrypts invalidly if not encrypted with the correct key, acts like a discount JWT.

Encrypted session tokens with embedded expiry will serve the needs of 99% of applications/services.


In that case why not use JWTs which do this for you? In your case you end up building a subset of JWTs yourself.


A subset of JWTs sounds great given the previous security vulnerabilities found in JWTs and the huge surface area they give you:

https://www.akto.io/blog/jwt-none-algorithm-test


This is not a huge surface area. I don't know why this keeps coming up, but, while a silly default, is incredibly straightforward to not configure / remove / test for.


JWTs are definitely a much larger surface area than simpler encrypted sessions storage and most people don’t need that.

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.


JWTs are complex.

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.


No idea why you're being downvoted. This is the truth.


These might predate JWTs.


Indeed, RFC 7519 dates from 2015 while both Rails and Django are older than that by a decade or more.


Revocation in this context almost always means "suspension of the validity of a token prior to expiration", so that's not really an answer.


Down below there is a link to an article by tptacek that says that these random session tokens are the simplest and most secure and you should use them if you are able to (which is true in a lot more cases than they are used)

https://fly.io/blog/api-tokens-a-tedious-survey/


Having had a lot of conversations on this topic in early-stage organizations, it's really common for developers to want to prematurely optimize and move away from random bearer tokens. Something feels "wrong" about them or something. Often we're talking about avoiding one db query per request in an application that is nowhere near any database-imposed bottlenecks.

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.


> Often we're talking about avoiding one db query per request in an application that is nowhere near any database-imposed bottlenecks.

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.


JWTs are nice because you can skip a DB call completely if it doesn’t validate, furthermore it provides a very convenient method for embedding a tiny bit of date (role and/or tenant id) for use both on the back and frontend.

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.


DynamoDB note: the TTL setting isn't timely. It'll remove your item when it gets around to it. You have to filter it on the get item call or client side. It's great for cleanup but useless as an expiration mechanism.


For sure, it’s a nice clean up eventually but I always filter TTY client side when using it.


It is cheaper to look up at the handful of recovered tokens than however many active sessions you have, assuming you have a substantial number of them, otherwise, it is a moot point, similar to microservices for websites with 3-4 digits traffic per hour.


While this is technically true, using an index for either lookup makes the lookup happen in O(log n)-ish time, so neither is slow, even if you have a large number of active sessions.


The issue is not individual lookup time, it is the aggregate cost of millions database lookup requests that you can avoid.


Ah, sorry I misunderstood what you were saying.

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 guess you could use token exchange[1] to get benefits of both.

[1]: https://www.rfc-editor.org/rfc/rfc8693.html


From a developer standpoint, I enjoy being able to look at the JWT and reason about its meaning without having direct access to the underlying session database, e.g. what user is this and when was it issued.

I understand that it's not inherently more secure or performant, but it is convenient.


It can be nice in a distributed app built with eventual consistency. Often ok if revocations take a short time to propagate but that's generally not true for session tokens.


Wouldn't using JWTs in this case permit for starting to chip away on the monolith and integrating side services in the authz offered by the core service?

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.


Sure, but that presupposes that breaking apart a monolith is the goal, or even that "integrating side services" is a positive improvement.

I resisted microservice mania when it emerged, and now I feel validated that the pendulum has swung back.


Nothing wrong about keeping a monolith if it doesn't get in the way.

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 :-)


> A place I worked at recently insisted on JWTs despite being a monolith and calling the database on every API endpoint.

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.


You're right, but the parent probably meant accessing the DB for every request for stuff other than revocation.


Assuming they don't manage to consolidate everything they need for a request into 1 query, one less query is still one less query. They might be cutting their db load in ~half if they had one auth query and one query for the action.


I did. Thanks :)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: