CWE-113: HTTP Response Splitting
Overview
HTTP Response Splitting occurs when untrusted user input is included in HTTP headers without proper validation or encoding, allowing attackers to inject CRLF characters and create additional headers or responses.
OWASP Classification
A05:2025 - Injection
Risk
High: Attackers can perform web cache poisoning, cross-site scripting (XSS), or redirect users to malicious sites by manipulating HTTP responses.
Remediation Steps
Core principle: Never allow untrusted input to influence HTTP response headers; header values must be validated or constructed by the server so they cannot introduce CRLF characters or alter response structure.
Locate the Vulnerable Header Construction
Review the security findings to identify where untrusted data is used in HTTP headers:
- Identify the source: Find where untrusted data enters (HTTP parameters, database, external APIs)
- Trace to the sink: Locate where data is set in HTTP response headers (Location, Set-Cookie, etc.)
- Check the data flow: Review each frame in the data_path for missing sanitization
- Understand the context: Determine which header is being set and its security implications
Remove or Encode CR/LF Characters
Sanitize all untrusted data before including in HTTP headers:
- Remove newline characters: Strip
\r(carriage return, %0D) and\n(line feed, %0A) from input - Remove null bytes: Also strip
\0(%00) which can terminate strings unexpectedly - Encode special characters: URL-encode if the header value permits it
- Use allowlist validation: For restricted header values (e.g., URLs), validate against expected format
Use Framework-Provided Header APIs
Prefer framework methods that handle header construction safely:
- Use framework redirect methods: Instead of manually setting Location header, use framework redirect functions
- Use cookie APIs: Use framework cookie-setting methods instead of manually constructing Set-Cookie headers
- Avoid manual header construction: Don't concatenate strings to build headers
- Let framework encode: Modern frameworks often encode header values automatically
Validate Input for Header Fields
Apply strict validation to all data that will be used in headers:
- Validate URLs: For Location headers, verify the URL is properly formatted and uses allowed schemes (http, https)
- Check length limits: Enforce maximum length for header values
- Use allowlists: For enumerated header values, validate against known-good list
- Reject unexpected characters: Block or encode characters that shouldn't appear in the specific header type
Monitor and Log Header Manipulation Attempts
Detect and alert on potential HTTP response splitting attacks:
- Log all header values: Record what data is being set in headers
- Alert on suspicious patterns: Monitor for CR/LF characters, multiple header separators, null bytes
- Track injection attempts: Log when validation rejects input containing newlines
- Implement rate limiting: Prevent automated attack attempts
Test with HTTP Response Splitting Payloads
Verify the fix prevents header injection:
- Test with CR/LF characters: Try input like
value%0d%0aInjected-Header: malicious - Test double encoding: Try
%0d%0a(URL encoded) and%250d%250a(double encoded) - Test header injection: Attempt to inject
Set-Cookieor other security-sensitive headers - Test response splitting: Try to inject a complete HTTP response with
%0d%0a%0d%0a<html>... - Verify legitimate redirects work: Ensure valid URLs still function correctly
Dynamic Scan Guidance
For guidance on remediating this CWE when detected by dynamic (DAST) scanners:
- Dynamic Scan Guidance - Analyzing DAST findings and mapping to source code
Common Vulnerable Patterns
Directly inserting user input into HTTP headers
Why this is vulnerable:
- Failing to validate or encode header values
Secure Patterns
Use Framework Redirect APIs (Best Practice)
# BEST: Use framework's redirect method (handles encoding automatically)
# Framework methods typically validate and encode header values
return redirect(validated_url)
Why this works: Modern framework redirect methods (like Response.redirect(), res.redirect(), redirect()) handle header construction safely by automatically encoding special characters, validating input, and preventing header injection. They construct the Location header internally without allowing raw user input to contaminate the header structure. This is the preferred approach because frameworks have battle-tested implementations that handle edge cases like null bytes, Unicode normalization, and encoding bypasses.
Validate and Sanitize for Header Context
# GOOD: Validate + sanitize based on header type
def safe_redirect(user_url):
# 1. Remove dangerous characters (necessary but not sufficient)
cleaned = user_url.replace('\r', '').replace('\n', '').replace('\0', '')
# 2. Validate based on header context
if header_is_location:
# For Location headers, validate as URL (see CWE-601)
if not is_safe_url(cleaned):
return default_safe_url
elif header_is_cookie:
# For Set-Cookie, validate cookie value format
if not is_valid_cookie_value(cleaned):
return None
# 3. Use framework API to set header (additional safety layer)
response.set_header('Location', cleaned)
return response
Why this works: This defense-in-depth approach combines multiple protections: (1) Removing CR/LF (\r, \n) and null bytes (\0) prevents basic header injection by eliminating characters used to create new headers or terminate strings. (2) Context-specific validation ensures the value is appropriate for the header type - Location headers should be valid URLs (validated per CWE-601 guidance), Set-Cookie values should match cookie syntax, custom headers should match expected patterns. (3) Using framework APIs to set headers (rather than manual string concatenation) provides an additional safety layer as frameworks often apply their own encoding. (4) Fail-closed behavior (returning default or None) ensures invalid input doesn't result in broken headers. This multi-layered approach prevents not just CRLF injection but also related attacks like open redirects (CWE-601) and cookie injection.
Manual Sanitization (When Framework APIs Unavailable)
# ACCEPTABLE: Manual sanitization if framework methods not available
def sanitize_header_value(value, header_name):
# Remove CRLF and null bytes
sanitized = value.replace('\r', '').replace('\n', '').replace('\0', '')
# Remove other control characters (ASCII 0-31 except space)
sanitized = ''.join(c for c in sanitized if ord(c) >= 32 or c == ' ')
# Header-specific validation
if header_name == 'Location':
# Validate URL format and restrict to local URLs
if not is_local_url(sanitized):
return '/' # Default safe redirect
return sanitized
# Usage
clean_value = sanitize_header_value(user_input, 'Location')
response.headers['Location'] = clean_value
Why this works: When framework methods aren't available, comprehensive manual sanitization is required. Removing CR (\r), LF (\n), and null bytes (\0) prevents header injection attacks. Additionally removing all control characters (ASCII 0-31) blocks other potential injection vectors like tab characters, form feeds, or vertical tabs that might be interpreted differently by proxies or clients. The header-specific validation (like is_local_url for Location headers) prevents related vulnerabilities - for Location headers this prevents open redirects (CWE-601). The fail-safe default ensures that invalid input results in a safe, known-good value rather than a broken or exploitable header. This pattern should only be used when modern framework APIs are truly unavailable.
Language-Specific Guidance
- C# - Response.Redirect, ContentDisposition.builder, header validation
- Java - Spring redirect methods, ResponseCookie.from, UriComponentsBuilder