Lion Cipher Lab
Harden an HTTP layer against response splitting (CRLF injection)
- AppSec / DevSecOps
- Advanced
- ~60 min
- Free · no account required
Why it matters
Response splitting is what happens when a server lets attacker-controlled text flow into the bytes of an HTTP response without neutralising the CR and LF characters that structure that response. Because CR+LF (\r\n) is the exact delimiter HTTP uses to separate one header from the next, and a doubled CR+LF ends the header block, a single reflected redirect URL like ?url=/next%0d%0a%0d%0aHTTP/1.1... can inject a forged header or splice an entire second response into the stream — poisoning a shared cache, planting a cookie, or delivering script to every later visitor. It is CWE-113, and it keeps…
What you'll learn
- explaining what a CRLF (\r\n) is and why it is the framing primitive that separates HTTP headers and ends the header block
- building a naive response serializer that reflects a user value into a header, and measuring the resulting split on the wire
- counting the concrete damage: injected extra headers and smuggled second responses across a set of malicious inputs
- hardening a header sink with an allowlist that rejects CR, LF, NUL and other control chars on the FINAL decoded value
- diagnosing the decode-order bug — validating before percent-decoding lets %0d%0a slip past and become a real CRLF downstream
- recognising the blocklist gap (bare \n, bare \r, NUL, Unicode line separators) and why an allowlist of the header grammar is safer
- placing the fix at the right layer: the HTTP library's header API forbids control chars (http.client / wsgiref reject \n), not per-endpoint string-building
- distinguishing response splitting (server output into a response) from request smuggling (front-end/back-end disagreement on request framing)
Steps in this lab
- What response splitting is — and the ground rules
- How a header gets poisoned: reflection + CRLF
- Set up: a terminal, Python, and a scratch folder
- Build the naive serializer AND an on-the-wire measurer
- Run it and MEASURE the naive damage
- The fix, proven: allowlist the header grammar
- Failure mode 1: the decode-order bug
- Failure mode 2, the right layer, and 'why this, not that'
- What you proved — and the defender's checklist