# CRLF injection into protocol headers

User input concatenated into headers or line-based protocol commands without stripping CR, LF, or quotes

_Area: Web injection and code execution. Seen in 14 disclosed reports; 12 confirmed bounties, median $1,340, range $125-$35,000; latest 2026-03-07. Updated 2026-10-23._

## How it works
A user-controlled value is concatenated into a header or a line-based protocol command (Set-Cookie, Location, Content-Disposition, an outbound fetch header, a plaintext protocol line) without removing CR, LF, or quote characters. The receiving parser treats the injected newline as the end of the current line, so the attacker appends headers or a whole new message. The broken assumption is that the value is "just data" — but the same value may also be parsed elsewhere (as an integer, a filename, a token), and the two paths disagree about where the value ends [E1](https://bugbountylibrary.com/r/blog%3Arobertchen-cc-blog-2021-04-03-github-pages-xss.md).

## Where it shows up
- A parameter reflected into Set-Cookie or Location, especially one also parsed as an integer or ID [E1](https://bugbountylibrary.com/r/blog%3Arobertchen-cc-blog-2021-04-03-github-pages-xss.md).
- Server-side fetches that put a user value into a request header (webhook tokens, auth headers) [E3](https://bugbountylibrary.com/r/h1%3A299473.md).
- Parallel code paths for old vs new protocol versions where only one sanitizes [E6](https://bugbountylibrary.com/r/h1%3A2279572.md).
- Filenames placed inside quoted Content-Disposition without escaping quotes [E7](https://bugbountylibrary.com/r/h1%3A1215263.md).
- URL paths reflected into response headers [E2](https://bugbountylibrary.com/r/blog%3Aportswigger-net-research-making-http-header-injection-critical-via-response-queue-poisoning.md)[E5](https://bugbountylibrary.com/r/h1%3A191380.md)[E8](https://bugbountylibrary.com/r/h1%3A858650.md).
- Values later reused as credentials in an outbound plaintext protocol handshake [E3](https://bugbountylibrary.com/r/h1%3A299473.md).

## How to review
Grep header-construction sites: Set-Cookie, Location, Content-Disposition, and outbound fetch headers. The invariant: after construction, the emitted header value contains no CR, LF, or unescaped quote, and the value is validated against an allowlist of characters, not a denylist. Distinguish response-header sinks (attacker controls what the victim's browser parses) from outbound-request-header sinks (attacker controls what an internal service parses) — the second is where severity jumps [E3](https://bugbountylibrary.com/r/h1%3A299473.md). For parser-differential inputs (null bytes, commas, semicolons), check whether one code path accepts the value while the other emits raw bytes [E1](https://bugbountylibrary.com/r/blog%3Arobertchen-cc-blog-2021-04-03-github-pages-xss.md)[E4](https://bugbountylibrary.com/r/blog%3Ablog-miki-it-2013-9-15-xsrf-cookie-setting-google.md). For server-side fetches, confirm the target cannot be an internal service where injected lines become commands [E3](https://bugbountylibrary.com/r/h1%3A299473.md). For pipelined or plaintext protocol sinks (SMTP, Redis, LDAP), check that each user value is confined to one line and cannot terminate the command [E3](https://bugbountylibrary.com/r/h1%3A299473.md).

## Variants seen
- Integer-parsed parameter also rendered verbatim into Set-Cookie, with null-byte terminator and case-insensitive cookie-prefix matching [E1](https://bugbountylibrary.com/r/blog%3Arobertchen-cc-blog-2021-04-03-github-pages-xss.md).
- CRLF in request path reflected upstream, escalating to request smuggling and response queue poisoning [E2](https://bugbountylibrary.com/r/blog%3Aportswigger-net-research-making-http-header-injection-critical-via-response-queue-poisoning.md).
- Newline in webhook token injected into a server-side fetch header, reaching internal Redis [E3](https://bugbountylibrary.com/r/h1%3A299473.md).
- Comma/semicolon injection into Set-Cookie enabling cookie folding (adjacent: folding, not CRLF) [E4](https://bugbountylibrary.com/r/blog%3Ablog-miki-it-2013-9-15-xsrf-cookie-setting-google.md).
- CRLF in URL path reflected into Set-Cookie, chained with stored XSS [E5](https://bugbountylibrary.com/r/h1%3A191380.md).
- Rack 3 array branch writing values verbatim while Rack 2 split on newlines [E6](https://bugbountylibrary.com/r/h1%3A2279572.md).
- Unescaped quote in Content-Disposition filename breaking the quoted-string [E7](https://bugbountylibrary.com/r/h1%3A1215263.md).
- Encoded %0d%0a in URL path reflected into response headers, enabling response splitting [E8](https://bugbountylibrary.com/r/h1%3A858650.md).

## Why fixes fail
Sanitization added to one branch or one field leaves siblings exposed: Rack 3 dropped hardening Rack 2 had [E6](https://bugbountylibrary.com/r/h1%3A2279572.md). Diff sibling fields and parallel code paths for the same sink; the unvalidated one is the bug.

## Impact and severity
Led to XSS on private pages, response queue poisoning leaking authenticated responses, RCE via Redis, and malware delivery via filename spoofing. Severity rises when the sink reaches internal services or other users' sessions [E2](https://bugbountylibrary.com/r/blog%3Aportswigger-net-research-making-http-header-injection-critical-via-response-queue-poisoning.md)[E3](https://bugbountylibrary.com/r/h1%3A299473.md).

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Arobertchen-cc-blog-2021-04-03-github-pages-xss.md
- E2: https://bugbountylibrary.com/r/blog%3Aportswigger-net-research-making-http-header-injection-critical-via-response-queue-poisoning.md
- E3: https://bugbountylibrary.com/r/h1%3A299473.md
- E4: https://bugbountylibrary.com/r/blog%3Ablog-miki-it-2013-9-15-xsrf-cookie-setting-google.md
- E5: https://bugbountylibrary.com/r/h1%3A191380.md
- E6: https://bugbountylibrary.com/r/h1%3A2279572.md
- E7: https://bugbountylibrary.com/r/h1%3A1215263.md
- E8: https://bugbountylibrary.com/r/h1%3A858650.md
