# CSRF Protection Bypass in Play Framework

Play Framework's Content-Type blacklist CSRF defense was bypassed by a malformed multipart boundary that Akka HTTP parsed as None, allowing cross-site forged POSTs.

- Payout: Research
- Severity: high
- Program: Play Framework
- Credited: Doyensec
- Disclosed: 2020-08-19
- CVE: CVE-2020-12480
- Write-up quality: 7/10
- Original: https://blog.doyensec.com/2020/08/20/playframework-csrf-bypass.html
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2020-08-20-playframework-csrf-bypass-html
- Tags: domain:web impact:integrity kind:research platform:research sev:high tech:java technique:fuzzing technique:parser-differential technique:source-review vuln:csrf

**Target:** Play/Akka HTTP Content-Type header parser and CSRF filter
**Root cause:** CSRF protection relied on a Content-Type blacklist; Akka HTTP's strict RFC 7230 parser rejects a boundary containing an extra semicolon and sets Content-Type to None, so the blacklist check is skipped while the browser still treats it as a simple request.
**Steps:**
1. Identify Play's legacy CSRF defense: contentType.blackList = [application/x-www-form-urlencoded, multipart/form-data, text/plain].
2. Note multipart/form-data is a simple request (no preflight) and its boundary is attacker-controlled.
3. Fuzz the boundary to find a value the browser accepts but Akka HTTP rejects: `Content-Type: multipart/form-data; boundary=------;---------------------139501139415121`.
4. Send cross-site XHR POST with that Content-Type and withCredentials=true; server logs 'Illegal header' and Content-Type resolves to None.
5. Request passes CSRF filter and the state-changing endpoint executes.
**How found:** Reasoned that bypass requires a simple request not matching the blacklist; focused on multipart/form-data because its boundary is attacker-controlled, then read Akka HTTP's HttpHeaderParser/HeaderParser RFC 7230 rules and fuzzed boundary values to find one the browser accepts but the parser rejects (None).
**Exploitation:** Attacker page issues XMLHttpRequest POST to victim Play app with the malformed multipart Content-Type and withCredentials=true; browser sends it as a simple request without preflight, Play's CSRF filter sees Content-Type None and skips the blacklist, so the forged state-changing request succeeds.
**Insight:** When a defense keys off a parsed header value, look for parser-differential inputs the browser accepts but the server-side parser rejects to None/empty, especially attacker-controlled parameters like multipart boundary.
