A practical proposal for migrating to safe long sessions on the web
developers.google.com
developers.google.com
"some websites don’t verify the user’s authentication on each request (i.e. there is no way to revoke the session cookie once issued)"
Which implies that if you do verify the session on the server and have a mechanism for invalidating these sessions on password change, etc, you can just use very long cookies and you're done.
There are very good reasons for doing that anyways, so no need for this hack.
If the password is changed, you can invalidate the session, but the cookie remains?
So you don't need to invalidate the auth tokens. You just need to be able to delete a session from the cache to do revocation on long sessions. And this works with just backend changes, no client hockey-pokey needed.
Nope. Let's say you have a rest endpoint that accepts a Pokemon id to add to your collection. If you cryptographically sign a cookie that says "I am this user id" you can assume the request to add the Pokemon to your collection is valid. There's other business logic to perform, sure, but "I am this user" is taken care of for you.
What Google is proposing is the ability to save a long-term cookie that says "I am this user" and a short-term cookie that says "I'm signed in". The requests periodically say "am I still signed in/am I still this user" and the server can periodically (eg not every request) look that up in cache/database.
This just moves the problem a bit - if you want to log out a user, you can just erase the cookie key from the server side; the cookie will be just random noise at that point.
This approach, of course, assumes that your keys are user specific, possibly generated from the password.
This is where the short/long-term cookie comes into play, and is actually a neat idea to take care of the "how do I periodically check that the user hasn't logged out/changed their password/invalidated their sessions/etc" problem that the cryptographically-signed cookie brings up.
If your cache is on a separate box and you have to make a network round trip, yeah it is expensive.
But if your cache is in-process, the access is cheap.
An authenticator is the product of an authentication algorithm, which can be as simple as a keyed SHA-3 hash (a hash over the data to be authenticated and a key known only to the verifier).
Session tokens virtually never use signatures. The only party that needs to verify them is the serverside of the application that generates them; they're opaque to clients.
Edit: I guess this is actually a good case for the 2CH described here. The long-session cookie could be cryptographically signed, while the short-session cookie could be authenticated with a shared key that gets rotated frequently so that a leak of the key cannot cause a long-term compromise.
Don't ever use public-key crypto unless you absolutely need it.
Also, how would you store a secret in a token without encryption? Or was that just a misstatement?
1. You encrypt and then authenticate data, such as user profile information, so that clients can neither read nor alter it. This doesn't involve public-key encryption.
2. You authenticate data, but don't encrypt it, because it isn't secret but must not be changed; this is what you'd do if the only semantic value being passed in your token as UUID, for instance.
3. You do neither of these things, and instead generate cryptographically random tokens of sufficient length that they can't be guessed, and then use those tokens as database lookup keys.
All three of these things are lightning fast on any decent processor, including the one on your phone.
You are very likely to cost yourself more security than you gain by trying to do anything more complicated than one of these three things. Again: don't ever use public-key crypto unless you really need it.
I guess for 2 and 3, you handle revocation by just deleting the relevant row or flagging a "revoked" column. For 1, add a unique id that you can add to a revocation list? Are there better/more typical ways to do this?
Agreed that public key crypto isn't really appropriate here.
If you want a stateless cookie, you won't be able to revoke it down the road. Which means if the cookie is long lived, someone who gets their hands on the signed cookie can impersonate a user for a long time.
If you cryptographically sign a cookie and it isn't stateless, you haven't really improved anything because you still have to look into up.
Cryptographically signing a cookie is great, especially for distributed applications, but it does have some properties that might not be desirable.
Once you do that you lose the stateless benefit of cryptographically signing the object, and at that point you could just store the whole object and give the user a lookup id to the object without the complexity of cryptographically signing anything.
Only if the collision of lookup IDs (accidental or malicious) is effectively impossible. If it's possible to generate a collision, then you've thrown away your security.
This would also effectively give every server the ability to issue auth tokens (and mutate them in the DB), which is not a great choice if you care about security. But if you're handing out unsigned lookup IDs, you probably don't.
And actually, yes, if I were using 128 byte identifiers for some reason, I would still be concerned about malicious parties being able to cause collisions. Securely generating random numbers does not seem easier than authenticating.
Generating 128 byte identifiers is ridiculously overkill. I can safely say if you generate 128 byte identifiers securely, the earth will cease to exist before a collision is found.
You would be safe generating cryptographically secure 128 bit identifiers and looking them up. It is trivial to generate IDs that will not collide.
Signing avoids the need to generate secure IDs, though, and can also avoid hitting to the DB for expired tokens. (Load from expired tokens is probably not a major concern, though.)
So that could be the difference between whatever you're using for storage, and a tiny, fully replicated in-memory structure. Depending on your overall scale, of course.
This has many advantages beyond the short/love-lived cookie stuff.
Make the signed cookie include user-id and date (and maybe a session id). Accept it without backend-check while it's less than X minutes old. Buf if it's older than that, check the backend to see if the user has invalidated sessions since it was generated. In the typical all-good case, generate a new signed cookie and put it in the response. No need for service workers or anything.
A web company I worked for a couple years ago implemented something almost exactly like this while I was there.
The former could be self-certifying (i.e., trusted if it's properly signed, with no auth service round-trip); the second could require a round-trip to the auth service). On a request, when the server see that the short-lived token has timed out, it checks the long-lived token; if still valid, then it reissues a short-lived token and sends it as a cookie value replacing the old short-lived token.
If multiple requests are in-flight, no matter — short-lived tokens require no session state, so all that happens is generating a few too many signatures.
Am I missing something?
Also, the approach with 2 cookies (statefull + stateless) is known for a long time. Eran Hammer (author of OAuth1 and OAuth2 specs) wrote about using this approach at Yahoo here: https://sideway.com/room/4Z (search for Yahoo).
An unstated goal (we probably should have stated it, looking back) was for sites to be able to easily move legacy sites with legacy endpoints to long sessions.
The service worker approach can effectively intercept requests to any endpoint and perform the re-generation of a short cookie if needed, without needing to change every page on the site or the legacy endpoints.
One additional benefit is that it minimizes the transmission of the long term token, which is generally good if you're worried about it somehow getting intercepted. You may or may not be too concerned about that risk though.
Why not just have the code that validates the session just do an `if (short_session != valid) { lookup_long_session_in_db() }` then return a fresh short session cookie with whatever request you're currently handling?
I know that there are benefits to using service workers in general, I guess I was just thinking about it in terms of adding a service worker to an application that isn't currently using them just to do the handshake they're suggesting. In which case it would be a lot of added complexity and wouldn't work across the board, whereas doing the check on the server side would add very little complexity and would work with every browser made since about 1995.
2. Your pseudo-code returns a legitimate short session cookie in response to gibberish. I'm hoping that this is just because you elided a lot of detail.
I would think that having all pages be able to handle auth is just a architecture question. Maybe I've just been too deep for too long in writing a webserver in Iron [0], but the way you handle auth in it, by using middleware and wrapping any endpoint that needs auth with an auth handler, makes doing something like this trivial.
2. Definitely left out a lot, I mean it doesn't really return anything at all. Just meant it as some shorthand pseudo code to make explaining it shorter. :)
[0] https://github.com/iron/iron
EDIT: Actually now I'm not sure if you're talking server-side or client-side, but I think the argument still stands. I was assuming auth "tokens" could actually be cookies, but even if not, your API library just needs to have a check to see the the request returned updated auth. It's still just in one place, handled in all calls, rather than just the single call that would otherwise handle it.
If you're running a small scale service, you're unlikely to build a dedicated login service. If you're running a large scale service, you probably should.
Edit: Based on Thomas's answers here, I am wrong about this, so I guess take my thoughts with a grain of salt.
I agree with dpark - an unstated goal of this proposal was for developers to be able to easily move legacy sites with legacy endpoints to long sessions without needing to update those endpoints or pages.
The service worker approach can effectively intercept requests to any endpoint and perform the re-generation of a short cookie if needed, without needing to change every page on the site or the legacy endpoints.
I wish that were true more often. There's a handful of native apps that don't remember my credentials and I have to go look them up on the desktop in my password manager. For example, I installed Pokémon Go but haven't looked into it further because I don't have my Google password memorized; it's a randomly generated password that I expect my computer to remember for me.
It seems like every native IoT widget controller I try wants me to remember more credentials.
Actually we also have a workaround to support Safari by trying to put stuff in LocalStorage that tells the other Tab that it recently got a new token and so on.
Then all you have to add is a way for password changes to invalidate the session.
I'm not saying it's good or secure, but it's the way I often see it done/suggested to be done.
My concern is privacy.
With apps, I know that this app doesn't know my identity from that app unless I explicitly give it permissions.