Hi from the Pages team! This was such a great find and can't thank you enough for helping ensure Private Pages is as secure as possible.
This report helped uncover:
- A bug in Openresty where `ngx.redirect` didn't handle unsafe characters [1]. While the fix is now in the latest version of Openresty, a quick patch was to build the URL safely before using it in the redirect.
- You should check for case sensitivity when reading `__Host` prefixed cookies, and verify the values against your expected format. It's possible for both `__HOST-Foo` and `__Host-Foo` cookies to exist, and only the `__Host` prefix requires the `Secure` and `HttpOnly` attributes [2]. In our case we strip all cookies at the edge using Varnish (VCL) to ensure no user-supplied cookies make it to our origin, and now we also ignore any "Secure" cookies that don't appear to have been set by our servers.
I'm not a security expert, but in node.js (specifically express.js) there's a concept of "signedCookies".
Usually you can just set a "httpOnly" flag to make sure client-side javascript can't mess with the cookie. But if you also sign the cookie, it further enforces this for any client tampering with the cookie manually too. Because only the server knows the secret for creating a new signature, if the client sends back a cookie that is modified in any way (including case sensitivity), it will be discarded. It should prevent the whole class of bugs caused by "unexpected format".
It's also a signed cookie, and we do the signature verification you mention if the format is correct, but if the format doesn't match, that cookie is discarded at the edge, before it even gets sent down to our app servers.
It's conceptually the same, but JWT implies a specific data format standard which might be a more involved change architecturally.
I'm mentioning the cookie signature stuff because it can be added in an almost blackbox way at the web framework level. Whenever you send back something with set-cookie, also set a signature. Whenever receiving a request, check for that signature too. Though I guess it's not a good idea to try to "roll your own" if your web framework doesn't already support this out of the box.
This report helped uncover:
- A bug in Openresty where `ngx.redirect` didn't handle unsafe characters [1]. While the fix is now in the latest version of Openresty, a quick patch was to build the URL safely before using it in the redirect.
- You should check for case sensitivity when reading `__Host` prefixed cookies, and verify the values against your expected format. It's possible for both `__HOST-Foo` and `__Host-Foo` cookies to exist, and only the `__Host` prefix requires the `Secure` and `HttpOnly` attributes [2]. In our case we strip all cookies at the edge using Varnish (VCL) to ensure no user-supplied cookies make it to our origin, and now we also ignore any "Secure" cookies that don't appear to have been set by our servers.
[1]: https://github.com/openresty/lua-nginx-module/pull/1654
[2]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Se...