Simon Koeck’s Google DevSite security writeup starts with an oversized Flask session cookie and ends with a $90,000 bug bounty. The useful lesson is how ordinary design choices composed into a broken trust boundary: the cookie carried the same secret used to sign it, and its contents eventually reached an executable deserializer.
The chain depended on three mistakes:
- Readable client state: a signed cookie contained a serialized OAuth flow, including its client secret. Signing provided no confidentiality.
- One secret doing two jobs: that OAuth credential also validated the Flask session signature, so recovering it enabled session forgery.
- Executable parsing: the callback unpickled the flow object after signature verification. A valid signature stopped meaning “server-authored” once the key was exposed.
Koeck’s proof is also a good debugging model. He independently verified the signing-key hypothesis, then used controlled time delays rather than shell commands to demonstrate execution. Missing cookies, damaged signatures, and missing flow entries supplied negative controls.
The remediation removes multiple failure paths:
- Rotate exposed credentials and invalidate affected sessions.
- Give deployments independent, server-held signing keys.
- Keep OAuth flow objects server-side; put only opaque references in cookies.
- Replace pickle across client boundaries with schema-validated, data-only serialization.
The takeaway is not merely “keep your signing key secret.” Confidentiality, integrity, and safe parsing are separate properties. A compromised integrity check should not turn an application state object into a program.