zohl / servant-auth-cookie

Authentication via encrypted cookies
BSD 3-Clause "New" or "Revised" License
23 stars 23 forks source link

Provide guidance on managing server keys #26

Closed MichaelXavier closed 7 years ago

MichaelXavier commented 7 years ago

So it looks like a ServerKey is a bytestring key with a built in expiration. I see that you need the key to encrypt/decrypt cookies. I also noticed that the example app generates a new key at application boot which generates a random sequence of bytes. Doesn't this mean that every time you boot the example app it generates a new key (and even when the key expires it generates a new key)? Wouldn't that mean if a user gets a session and then you reboot the app that you'd no longer be able to decrypt the user's cookie and their session would invalidate? I'm no cryptography expert so let me know if I've taken a wrong turn somewhere.

It seems like what you'd usually want to do is generate a key in a script or something, dump it to disk and distribute that to all your app servers in production and not use the expiration feature. Then when you wanted to expire the key you'd regenerate that file (though this would suck because it would boot everyone on the old key from the system, even if they just got their session).

zohl commented 7 years ago

Hello,

that's right, you've understood everything correctly. At the moment, key renewal mechanism might cause troubles both for end-users and (in case of multiple application instances) for system administrators. Therefore the preferred way would be to use mkServerKeyFromBytes.

So, we have two issues: 1) There should be posibillity to renew keys with specific sequences of bytes (so we can uniformly update keys in multiple application instances). 2) Users should not be disturbed by key renewals.

I think I should rewrite ServerKey to make it more customizable, so it will be possible to provide user-defined function to renew the key.

As for the second problem, it will be a bit more complicated. Instead of one key we can keep a mapping from time intervals to keys and mark one of the keys as a preferred one. When a user sends a request, we find corresponding key and, if the key has expired, update the cookies (encrypted with the preferred key). Thus we graciously renew the key. In case of an emergency we can simply remove the compromised key and users' cookies will be invalidated.

Thanks for pointing out the issue, I'll be working on it!

MichaelXavier commented 7 years ago

I'm really not sure of the common approaches as I've usually just used server-side, database-backed sessions so it was a different set of issues to overcome. However, I think at minimum having a preferred sequence of keys would be a great step forward. I guess you could do that today if you carefully catch exceptions. If I was storing my key on disk I could periodically swap the key on file so I always have a cookies_key and cookies_key.old on disk and I could keep rotating those out at a schedule that makes sense. I could keep the server key list under a TVar or something like that in the server if I needed it to be updatable while the app is still running.

MichaelXavier commented 7 years ago

Oh, and I guess in the mean time if you think you may be thinking about this for a while it may be worth just a quick comment in the readme or the example that if you generate a new key on every server boot it will invalidate old sessions. I suspected this was the case when I was implementing it but you can imagine a case where someone follows the example, tests that sessions work (they will) but doesn't test they persist through restarts and have a bad time.

zohl commented 7 years ago

Done.

The key management was completely changed and got a little bit complicated. I reflected those changes on the demo example to give an idea, how to use and extend it. I also created wiki to explain it (and many other things) in plain text. Hope it helps!

MichaelXavier commented 7 years ago

Thanks!