What is HTTP Response Splitting?
HTTP Response Splitting (CRLF Injection) occurs when user-supplied data containing carriage return (\r, CR) and line feed (\n, LF) characters is included in an HTTP response header without sanitization. Since HTTP headers are separated by \r\n sequences, injecting these characters allows an attacker to insert arbitrary headers or — by injecting a double CRLF — create an entirely new response body.
Consequences include: session cookie hijacking via injected Set-Cookie headers, cross-site scripting by injecting a new HTML body, cache poisoning that causes proxy servers to serve attacker-controlled content to other users, and open redirect via injected Location headers.
How exploitation works
An application sets a redirect header using user input:
GET /redirect?url=https://example.com HTTP/1.1
An attacker URL-encodes CRLF characters in the url parameter:
GET /redirect?url=https://example.com%0d%0aSet-Cookie:%20session=hijacked HTTP/1.1
The server produces:
HTTP/1.1 302 Found
Location: https://example.com
Set-Cookie: session=hijacked ← Injected by attacker
With a double CRLF (%0d%0a%0d%0a), the attacker can inject an entirely new HTTP response body, effectively conducting reflected XSS through header injection.
Vulnerable code examples
Java / Spring — unsanitized redirect
// VULNERABLE: User-controlled URL set directly in Location header
@GetMapping("/redirect")
public void redirect(HttpServletResponse response,
@RequestParam String url) throws IOException {
response.setHeader("Location", url); // CRLF in url splits the response
response.setStatus(302);
}
PHP — header() with user input
// VULNERABLE: User input passed directly to header()
$lang = $_GET['lang']; // attacker sends "en\r\nSet-Cookie: admin=1"
header("Content-Language: " . $lang);
Secure code examples
Java — CRLF stripping
// SECURE: Strip CR/LF from any value before setting in a header
@GetMapping("/redirect")
public void redirect(HttpServletResponse response,
@RequestParam String url) throws IOException {
String safeUrl = url.replaceAll("[\r\n]", ""); // Strip CRLF
// Also validate it's a relative or trusted URL
if (!isAllowedRedirect(safeUrl)) safeUrl = "/";
response.sendRedirect(safeUrl); // sendRedirect also encodes the value
}
Python / Django — validate header values
from django.http import HttpResponseRedirect
import re
def redirect_view(request):
target = request.GET.get('url', '/')
# SECURE: Remove CRLF characters and restrict to relative URLs
target = re.sub(r'[\r\n]', '', target)
if not target.startswith('/') or '//' in target:
target = '/'
return HttpResponseRedirect(target)
C# / ASP.NET Core — use framework redirect method
// SECURE: Use framework's Redirect() method which encodes the URL
// Never set the Location header manually with user input
[HttpGet("redirect")]
public IActionResult RedirectTo(string url)
{
if (Url.IsLocalUrl(url))
return Redirect(url); // Framework handles encoding
return RedirectToAction("Index", "Home");
}
What Offensive360 detects
- User input in
setHeader()/addHeader()— Direct use of tainted strings in HTTP header-setting calls - Unsanitized
header()calls (PHP) — User input passed to PHP’sheader()function without CRLF filtering - Cookie value injection — User-controlled data set as cookie values without CR/LF validation
- Location header injection —
Response.Redirect()orLocation:headers containing unvalidated user input
Remediation guidance
-
Strip CRLF from all header values — Remove or encode
\rand\nfrom any user-supplied string before including it in an HTTP header. -
Use framework redirect methods — Prefer
Redirect(),sendRedirect(), orHttpResponseRedirect()over manually setting theLocationheader with user input. -
Validate redirect targets — Ensure redirect URLs are relative, match an allow-list, or have been signed/validated before use.
-
Use modern framework defaults — ASP.NET Core and Spring will automatically reject headers containing CRLF characters in
Response.Headersassignments. -
Apply output encoding — URL-encode values before including them in headers; encoded
%0D%0Adoes not function as a CRLF at the HTTP protocol level.