Lion Cipher Lab
Lock down direct-to-S3 browser uploads with a presigned POST policy (built and enforced offline)
- Cloud Security
- Intermediate
- ~75 min
- Free · no account required
Why it matters
Almost every modern app that takes photos, documents or videos lets the browser upload straight to cloud storage instead of streaming gigabytes through its own servers. On AWS the classic way to do that is a browser-based POST: your backend hands the browser an HTML form whose hidden fields carry a POST policy (a small JSON document listing what this upload is allowed to be) and a signature over it. S3 checks the form against the policy and stores the file. The policy is the only thing standing between 'alice uploads her avatar' and 'alice overwrites bob's files, parks a 2 GB file on your…
What you'll learn
- Explaining how a browser uploads straight to S3 with an HTML form, a base64 POST policy and a SigV4 signature, with no server in the byte path
- Minting a per-user POST policy and signing it with SigV4, where the StringToSign is the base64 policy itself
- Proving a POST policy signature byte-for-byte against AWS's documented example, including why CRLF versus LF line endings change the signature
- Writing an offline verifier for the documented POST policy rules: expiration, eq, starts-with, content-length-range, the Content-Type comma-list rule, ${filename} expansion and the every-field-must-be-in-the-policy rule
- Measuring an abuse matrix (prefix escape, oversize file, text/html and SVG uploads, form reuse, extra fields, bucket swap, policy edits) against loose, typical and tight policies
- Choosing between a POST policy and a presigned PUT URL for uploads, and naming what neither one can check
Steps in this lab
- What a browser POST upload is, and why apps use it
- Recap the SigV4 signing key, then the one big difference for POST
- Anatomy of a POST policy: expiration, conditions, and five rules to remember
- Write postpolicy.py: the backend that mints a form for one user
- Prove your signing matches AWS's documented example
- Write verify.py: an offline emulation of S3's checks (and what it is not)
- Write matrix.py and measure: three policies against nine abuse attempts
- Read the matrix: which line of the policy stopped which attack
- POST policy versus a presigned PUT URL (and the presigned GET sibling lab)
- The honest negatives: what no POST policy can stop
- The judgment: when to use a POST policy, and how a pro writes one