HTTP Response Splitting
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5), because detection_hints.tools lists semgrep and owasp-zap — both specialist SAST/DAST tools. The vulnerability is not caught by a compiler or default linter; it requires either a dedicated static analysis rule (semgrep) or dynamic scanning (owasp-zap) to surface reliably.
Closest to 'simple parameterised fix' (e3), because the quick_fix describes stripping \r and \n from user input before passing to header() calls — a small, targeted change. For PHP 7.4+ it is essentially a version-upgrade fix; for older PHP it is a consistent input-sanitisation pattern applied wherever header() accepts user-controlled values, which may touch a handful of call sites but stays within one component.
Closest to 'localised tax' (b3), because applies_to scopes this to web and api contexts only, not CLI or queue workers. The fix burden falls on places that reflect user input into HTTP headers — a relatively localised concern rather than something that shapes the whole architecture.
Closest to 'serious trap (contradicts how a similar concept works elsewhere)' (t7), because the misconception explicitly states developers believe modern frameworks are immune, and common_mistakes include trusting that PHP's header() sanitises input (it does not in all versions) and missing that URL-decoded values reintroduce the hazard. The 'obvious' assumption — that the framework or built-in function handles this — is wrong in many real scenarios, making it a serious cognitive trap.
Also Known As
TL;DR
Explanation
HTTP Response Splitting is an escalation of CRLF injection. By injecting \r\n\r\n into a header value, an attacker can terminate the first response and craft an entirely fabricated second response — including its own headers and body. Shared proxies and caches may store the attacker-injected second response and serve it to other users. Modern PHP (7.4+) strips newlines from header() calls, but older code or custom header-writing functions remain vulnerable. Always sanitise header values and use framework-level response objects rather than raw header() calls.
How It's Exploited
Common Misconception
Why It Matters
Common Mistakes
- Reflecting user input into any HTTP header without stripping \r and \n characters.
- URL-decoding values before passing to header() without re-stripping control characters.
- Not realising this is the underlying mechanism behind CRLF injection and header injection.
- Trusting that PHP's header() function sanitises its input — it does not in all versions.
Code Examples
// User input injected into header — response splitting:
$lang = $_GET['lang']; // Attacker: en\r\nHTTP/1.1 200 OK\r\n...
header('Content-Language: ' . $lang);
// Results in a second fabricated HTTP response
// Safe:
$lang = preg_replace('/[\r\n]/', '', $_GET['lang']);
header('Content-Language: ' . $lang);
// Validate and encode header values before sending:
function safeRedirect(string $url): void {
// Validate URL — only allow safe schemes and known domains:
$parsed = parse_url($url);
if (!in_array($parsed['scheme'] ?? '', ['http', 'https'], true)) {
throw new InvalidArgumentException('Invalid URL scheme');
}
// PHP 7.4+ header() strips CR/LF — but be explicit:
$safeUrl = preg_replace('/[\r\n]/', '', $url);
header('Location: ' . $safeUrl, true, 302);
exit;
}
// For custom headers:
$safeValue = preg_replace('/[\r\n\0]/', '', $userInput);
header('X-Custom: ' . $safeValue);