Security Cipher

Security Resources

⌘K
  1. Home
  2. Security Resources
  3. Vulnerability Explain
  4. CRLF Injection and HTTP Response Splitting

CRLF Injection and HTTP Response Splitting

This Vulnerability Explain post covers CRLF Injection and the HTTP Response Splitting it enables. It is a subtle flaw built on two invisible characters – carriage return and line feed – that structure HTTP messages. Abusing them lets attackers forge headers, poison caches, and more. Let’s dig in.

What is CRLF Injection?

CRLF Injection happens when an application places user input into an HTTP response header (or a log, or another CRLF-delimited context) without stripping the carriage return (\r) and line feed (\n) characters that separate headers and lines.

Because HTTP uses CRLF to delimit headers, injecting \r\n into a value that ends up in a header lets an attacker add their own headers or even start a new response body. For example, a redirect that reflects a user value into the Location header can be abused to inject a Set-Cookie header or split the response entirely.

How does CRLF Injection work?

CRLF injection escalates from two characters to serious impact like this:

  • Input In a Header Context
    • User input flows into a response header, a redirect Location, a cookie, or a log line.
  • CRLF Not Stripped
    • The application does not remove or encode \r and \n characters.
  • Header Injection
    • The attacker injects \r\n followed by their own header, such as Set-Cookie or Content-Length.
  • Response Splitting
    • With a double CRLF, the attacker can terminate the headers and inject a full second response body.
  • Impact
    • Outcomes include cache poisoning, reflected XSS via injected body, cookie fixation, and log forging.

CRLF injection is a reminder that protocol delimiters are data too. If the user can smuggle the characters that structure a message, they can rewrite the message.

CRLF injection diagram showing injected carriage return and line feed characters adding malicious HTTP headers

How CRLF injection works: injected CR/LF characters add or split HTTP headers and responses.

Tools and Techniques for CRLF Injection Testing

Testing focuses on injecting encoded CR/LF sequences into reflected header values.

Manual Testing Methodologies

  • Encoded CRLF Probes – Inject %0d%0a sequences into parameters that affect headers and watch for new headers in the response.
  • Redirect Abuse – Target endpoints that reflect input into the Location header.
  • Cookie Injection – Try injecting a Set-Cookie header to test fixation.
  • Log Injection Check – Inject newlines into values written to logs to test for forged log entries.

Automated Scanning Tools

  • Oralyzer – Detects CRLF injection and open redirects.
  • CRLFsuite – A fast, dedicated CRLF injection scanner.
  • Burp Suite Scanner – Flags header injection and response splitting.
  • Nuclei – Templates detect CRLF injection patterns.

CRLF Injection Protection Mechanisms

Best Practices for Secure Coding

  • Strip CR and LF From Header Values
    • Description: Remove or reject \r and \n in any value used in a header.
    • Benefits: Eliminates header injection and response splitting.
    • Implementation Tip: Reject the whole request if a header value contains control characters.
  • Use Safe Framework APIs
    • Description: Set headers and redirects through framework methods that encode or validate values.
    • Benefits: Modern frameworks block CRLF in headers by default.
    • Implementation Tip: Avoid manually constructing raw header strings.
  • Encode for Logs
    • Description: Sanitize newlines before writing user input to logs.
    • Benefits: Prevents forged or misleading log entries.
    • Implementation Tip: Replace control characters with safe escapes.

Best Practices for Organizations

  • Modern Frameworks
    • Use up-to-date servers and frameworks that reject CRLF in headers.
    • Keep HTTP libraries patched.
  • WAF Rules
    • Deploy rules that flag encoded CR/LF in parameters.
    • Monitor for unexpected response headers.
  • Testing
    • Add CRLF checks to DAST.
    • Review any code that sets headers from input.

Top CRLF Injection payloads used by Security Researchers

As a security researcher, knowing the most common payloads helps you detect and prevent these attacks. Use this knowledge ethically and only on systems you are authorized to test. Some sample payloads are shown below.

// Inject a header via CRLF
/redirect?url=%0d%0aSet-Cookie:%20sessionid=attacker
// Full HTTP response splitting
/page?x=%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0a%0d%0a<script>alert(1)</script>
// Cache poisoning header injection
/q=value%0d%0aX-Forwarded-Host:%20evil.com
// Log forging via newline
username=admin%0d%0a[INFO]%20fake%20log%20entry

Real-World Example: Cookie Injection via a Redirect

An app reflected a returnUrl parameter into the Location header of a 302 redirect without filtering control characters.

A tester supplied returnUrl=%0d%0aSet-Cookie:%20sessionid=fixated. The server emitted a Set-Cookie header the attacker controlled, enabling a session fixation attack against victims who followed the link.

The fix rejected any redirect value containing CR or LF and used the framework’s safe redirect method. CRLF injection turns two invisible characters into real attacks – so they must never survive into a header.

Vulnerable and secure code of CRLF Injection

The following example shows the contrast between vulnerable and secure code for CRLF Injection. It helps you see how the flaw creeps into real code and the changes that shut it down.

🥺 Vulnerable Code:

# Vulnerable: raw user value placed into a response header
@app.route("/redirect")
def do_redirect():
    url = request.args.get("url", "")
    resp = make_response("", 302)
    # url may contain \r\n to inject extra headers
    resp.headers["Location"] = url
    return resp
  • CR/LF characters in url are written straight into the Location header.
  • An attacker can append headers like Set-Cookie or split the response.

😎 Secure Code:

# Secure: reject control characters and use safe redirect
@app.route("/redirect")
def do_redirect():
    url = request.args.get("url", "")
    if "\r" in url or "\n" in url:
        abort(400)
    # only allow safe relative paths
    if not (url.startswith("/") and not url.startswith("//")):
        url = "/dashboard"
    return redirect(url)
  • Requests containing CR or LF are rejected before any header is set.
  • Using the framework redirect with a relative path avoids manual header building.

Conclusion

CRLF injection looks minor until two control characters become forged headers, poisoned caches, and fixated sessions. Strip or reject CR and LF in any value that reaches a header, set headers and redirects through safe framework APIs, and sanitize newlines before logging. Treat the characters that structure HTTP as off-limits in user data and the whole class of attacks disappears.

How can we help?