Comment by atoav
One question I always wondered about with cookie signing is: Why not store the user and the cookie in a database and check against that when they try to present it to you? Performance reasons?
One question I always wondered about with cookie signing is: Why not store the user and the cookie in a database and check against that when they try to present it to you? Performance reasons?
Back in the Justin.tv days, we used this for some messages that were passed by the client between two of our systems: The main authentication was done by the web stack which gave the client an HMAC-signed viewing authorization. That was then passed on to the video servers which knew how to check the authorization but weren’t hooked up to the main DB.
Setting things up this way meant that we didn’t need to muck about with the video server code whenever we made policy changes as well as isolating the video system from web stack failures— If the web servers or DB went down, no new viewers could start up a stream but anyone already watching could continue uninterrupted.
Premature optimisation. We have a diverse set of clients but of all the ones I've audited with JWT and similar crypto-solutions, not one (of those that used sessions at all, not like a CDN or so) could not have run on a single database server. Some more comfortably than others, but also embedded devices with a handful of users at most will use cryptographic sessions nowadays. Some also choose to pack a session cookie into the JWT data and now you've got two things to validate instead of one
I understand it's nice to never have to worry about it regardless of scale, but generating sessions with an established CSPRNG and being able to invalidate them at will is an order of magnitude simpler. It's also standard and abstracted away for you already if you use any framework
This is how many (most?) session cookies work. Track the data on the backend, only send an identifier to the frontend.
The JWT and similar cookies exist for when you want to do scaling and such. You don't need much more than a user ID and a user name for many pages of a web application, your database may be in another continent, so you may as well store some variables in the client side. This has the added benefit of being able to put down as many frontends as you may need, integrating nicely with technologies like Kubernetes that can spawn more workers if the existing workers get overloaded.
By also encrypting the cookie, you can get rid of most of the backend state management, even for variables that should be hidden from the user, and simply decrypt+mutate+encrypt the cookie passed back and forth with every request, stuffing as many encrypted variables in there as can you can make fit.
They're also useful for signing in to other websites without the backend needing to do a bunch of callbacks. If a user of website A wants to authenticate with website B, and website B trusts website A, simply verifying the cookie with the public key (and a timestamp, maybe a n\_once, etc.) of website A can be enough to prove that the user is logged into website A. You can stuff that cookie into a GET request through a simple redirect, saving you the trouble of setting up security headers on both ends to permit cross-website cookie exchanges.
In most cases, signed cookies are kind of overkill. If all your application has is a single backend, a single database, and a single frontend, just use session cookies. This also helps protect against pitfalls in many common signed cookie variants and their frameworks.
Originally it was about scalability - signed/encrypted cookies are stateless, and hence (in theory) allow easy horizontal elastic scaling: just share the key with the new nodes. But I suspect that in a lot of cases now it is because it is easier initially to throw a key into an environment variable than standup a database, sort out caching, etc. It’s only later that you start thinking about revocation and idle timeouts and key rotation and all the other stuff that it becomes clear that it's not that simple to do well.
It's mostly about performance. If you can store all the required info about the user inside the cookie then you can avoid a DB query roundtrip before sending a response.
Now that your cookie looks like this (probably also base64 encoded):
You don't have to hit the DB to display "Hi John" to the user and hide the jucy "Admin" panel. Without HMAC, an attacker could flip the "is_admin" boolean in the cookie.You could also create a cookie that is just random bytes
and then store it in a DB table with similar info but now you would have to query that table for each request. For small sites it doesn't matter much and if it becomes a problem you can quite easily move that info into a faster key-value store like Redis. And when Redis also becomes too slow you are forced to move to JSON Web Tokens (JWT) witch is just a more standardized base64 encoded json wrapped with HMAC to avoid querying a database for each request.But even if you are using random bytes as your session identifier, you should still wrap it in a HMAC so that you can drop invalid sessions early. Just for making it harder for someone to DDOS your DB.