Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Avoid resetting the session key with each call to put/4 (#72)
What changed? ============ After upgrading to the new v0.7.0 of redbird, I found I consistently got `Plug.Conn.CookieOverflowErrors` after a few requests. This is strange—the session key is supposed to be stable, yet it was changing and growing in size with each request. The problem ------------- The redis key gets re-signed by Redbird.Crypto.sign with every call to put/4—even if that key was already signed, leading to a new (larger) key with each request! This stems from calling `Redbird.Key.sign_key/2` unconditionally (as of 299906f), which results in signing the already-signed user-supplied key. We end up with a key with arbitrary levels of nested signatures. This is bad in its own right (redbird isn't supposed to move to a new key each `put`), but also leads to the key growing without bound, as each new layer of signature results in a somewhat-longer key. Eventually, this overflows the maximum length of 4096 bytes which plug is willing to allow in a cookie, leading to a `Plug.Conn.CookieOverflowError`. The fix ------- The fix is to sign the key exactly once, right after generating it, then verify that the key is signed properly in `put/4` in the same way as in `get/3` or `delete/2`. Implementation notes ---------------------- The function previously known as Redbird.Key.deletable?/2 is now Redbird.Key.accessible?/2, and is used in put/4, get/3, and delete/2. A key is now signed exactly once, immediately after being generated, and verified each time it is used. This fixes the bug described in the previous commit, where the session key is re-signed with each call to put/4, causing it to grow without bound and eventually overflow. This leaves us with the question of what to do if a key passed to put/4 is invalid. At that point, the redis key sent in the cookie can't be trusted (e.g. it's unsigned or signed incorrectly). We can't allow malicious users to set arbitrary redis keys. Therefore, if the signature can't be validate, we treat it as though it was never sent. So, we start over with a new generated key in the same way as when the key isn't specified, and that's the key we return. Keeping expiration in sync with Plug.Crypto -------------------------------------------- We use `Plug.Crypto` to check a key's validity. But Plug.Crypto defaults to a validity period of 24 hours. So we would effectively expire sessions after 24 hours, irrespective of redbird's configured expiration time. To address that, we pass our expiration time through as the max_age when verifying the signature. That way, we avoid expiring sessions early when redbird is configured to keep session for longer than 24 hours (as indeed it is by default.)
- Loading branch information