Lion Cipher Lab
Harden a serverless event handler against injection: the event payload is untrusted input (offline, no AWS account)
- Cloud Security
- Advanced
- ~90 min
- Free · no account required
Why it matters
Every serverless security guide you have read starts with the execution role, the VPC, or the dependencies in your deployment package — and Lion Cipher already has a lab for each of those. Almost none of them start with the thing that actually invokes your function: the event. A Lambda handler does not receive an HTTP request that you instinctively distrust; it receives a JSON document that AWS itself assembled and delivered, signed, from a service you configured, and that framing quietly convinces engineers the contents are safe. They are not. An S3 ObjectCreated event carries an object key…
What you'll learn
- reading a real AWS event envelope (S3 ObjectCreated:Put, SQS-with-SNS-in-the-body) and identifying every attacker-controlled field in it
- the trusted-event-source fallacy: AWS authentically delivered the event, but an unauthenticated uploader chose its contents
- four injection classes carried by the event, each measured: OS command injection (CWE-78), log injection (CWE-117), format-string/template injection, and SQL injection (CWE-89)
- the decode-ORDER bug: S3 URL-encodes object keys and '+' means space, so validating before decoding lets %2E%2E%2F sail through an allowlist
- decoding exactly once, and rejecting double-encoded input rather than decoding in a loop
- allowlisting a key grammar instead of blocklisting bad characters, and why the blocklist always loses
- replacing subprocess(shell=True) with an argv list so shell metacharacters stop being metacharacters
- structured JSON logging: emitting untrusted data as a VALUE so a newline cannot forge a downstream record
- parameterized SQL queries as a structural fix, not a filter
- designing a per-function least-privilege execution role so a successful injection reaches nothing
Steps in this lab
- The gap this lab fills: the event is the input nobody validates
- Open a terminal and check Python (assume you have never done this)
- The five words you need before you read any of this code
- What the event actually looks like — and the encoding nobody reads about
- Write the program
- Run it, and read STAGE 1: four for four
- Why each of the four worked (and what the attacker actually had to do)
- STAGE 2 — the decode-ORDER bug, which is the one you will actually write
- STAGE 3 — the hardened pipeline, and each control measured alone
- Why this, not that: allowlist the grammar, do not blocklist the characters
- The trusted-event-source fallacy, and the role that bounds the damage
- STAGE 4 — the poison pill: at-least-once, idempotency and the dead-letter queue
- Read the scoreboard, then clean up
- When NOT to reach for this, and how a pro does it differently