Lion Cipher Lab
Exploit and fix a CORS misconfiguration (in your own lab)
- AppSec / DevSecOps
- Intermediate
- ~50 min
- Free · no account required
Why it matters
CORS misconfiguration is one of the most common and most misunderstood web bugs, because the mechanism is subtle: browsers, by default, let a page on one site SEND a request to another site but BLOCK the page from READING the response — that default is the Same-Origin Policy, and it is what stops evil.example from silently reading your webmail. CORS (Cross-Origin Resource Sharing) is the controlled way a server relaxes that rule, by returning an Access-Control-Allow-Origin header that names which origins are allowed to read its responses. The dangerous mistake is to REFLECT whatever Origin…
What you'll learn
- explaining the Same-Origin Policy and CORS: by default a browser lets JavaScript SEND a cross-origin request but BLOCKS it from READING the response; CORS is how a server opts specific origins back in with the Access-Control-Allow-Origin header
- defining an ORIGIN precisely — scheme + host + port (https://app.example.com:443) — and seeing that http vs https, a different subdomain, or a different port are all different origins
- building a tiny vulnerable Flask API on your own machine that REFLECTS the request's Origin header straight into Access-Control-Allow-Origin and also sends Access-Control-Allow-Credentials: true
- using curl to send Origin: https://evil.example and watch the server reflect it back in Access-Control-Allow-Credentials: true + Access-Control-Allow-Origin — proving any site could read authenticated responses cross-origin
- triggering and reading a CORS PREFLIGHT: an OPTIONS request the browser sends first (with Access-Control-Request-Method/Headers) to ask permission before a non-simple cross-origin request
- fixing it with a strict ALLOWLIST of trusted origins, echoing back only an exact match, never reflecting arbitrary Origin, and adding Vary: Origin so caches do not serve one origin's header to another
- knowing why the wildcard Access-Control-Allow-Origin: * can NEVER be combined with credentials — the browser rejects that pairing — and why the literal Origin 'null' must not be trusted (sandboxed iframes, local files, and redirects can send it)
- explaining why a reflected-Origin regex is a fragile fix (evil-example.com, trusted.com.evil.com, and suffix/prefix bugs slip through) and why CORS is a browser-enforced READ protection, NOT a server-side authorization control — curl ignores it, so CORS is not a firewall
Steps in this lab
- The vocabulary: origin, Same-Origin Policy, and what CORS actually does
- Set up: confirm Python, make a folder, install Flask
- Build the VULNERABLE API (it reflects any Origin, with credentials)
- Prove the misconfiguration (watch your untrusted Origin come back)
- Why reflecting the Origin (or regex-matching it) is the wrong fix
- Fix it: an exact allowlist, no reflection, no '*'+credentials, add Vary
- What you did — and the reflex to keep