Overview
CRLF Injection occurs when an attacker can inject Carriage Return (CR, %0d, \r) and Line Feed (LF, %0a, \n) characters into an application’s output that is included in an HTTP response header. Since CR+LF sequences (\r\n) are used to separate headers and delimit the start of the response body in HTTP/1.1, injecting them allows an attacker to add fake headers or even inject content into the response body. This can lead to HTTP Response Splitting, Cross-Site Scripting (XSS), session fixation, or cache poisoning. 📄✂️
Business Impact
CRLF Injection enables several other attacks:- Cross-Site Scripting (XSS): By injecting CRLF sequences followed by HTML/JavaScript into the response body.
- Session Fixation: By injecting a
Set-Cookieheader with a session ID they control. - Cache Poisoning: By crafting a response that gets stored in intermediate caches, affecting other users.
- Phishing/Defacement: By injecting custom content into the response body.
- Information Disclosure: By manipulating headers (e.g.,
Location) to redirect requests or expose internal details.
Reference Details
CWE ID: CWE-113
OWASP Top 10 (2021): A03:2021 - Injection
Severity: Medium to High (depending on the resulting attack)
Framework-Specific Analysis and Remediation
This vulnerability happens when user-supplied data is directly included in HTTP response headers without proper sanitization to remove or encode CR (\r) and LF (\n) characters. Common sinks include custom header values, redirect URLs (Location header), and cookie values (Set-Cookie header).
Modern web frameworks and libraries often provide some level of automatic protection by validating or encoding header values, but vulnerabilities can still occur in custom code or older/misconfigured setups. The primary defense is output encoding/sanitization specifically targeting CR and LF characters in any data destined for headers.
- Python
- Java
- .NET(C#)
- PHP
- Node.js
- Ruby
Framework Context
Manually setting headers in Django/Flask using user input without sanitization. Django’s built-inHttpResponseRedirect generally handles the Location header safely, but custom headers are a risk.Vulnerable Scenario 1: Custom Header Reflection
A view reflects a user-provided parameter in a custom header.Vulnerable Scenario 2: Unvalidated Redirect URL
Constructing a redirect URL using potentially tainted input (less common with Django’s built-in redirect, but possible in custom logic).Mitigation and Best Practices
Avoid reflecting user input directly into headers. If necessary, sanitize the input by removing or encoding\r and \n characters. Use Django’s built-in HttpResponseRedirect or redirect() shortcut which handles URL validation for the Location header.Secure Code Example
Testing Strategy
Identify all instances where user input is incorporated into response headers (response['Header-Name'] = user_input). Submit URL-encoded CRLF sequences (%0d%0a) followed by test headers (e.g., Injected-Header: test) or HTML content (%0d%0a%0d%0a<script>...). Use curl -v or browser developer tools to inspect the raw response headers and body for unexpected content.
