The go-to resource for upgrading Ruby, Rails, and your dependencies.

CVE-2008-5189: Ruby on Rails CRLF Injection


In the early days of telegraphy, operators used specific control characters to signal the end of a message or the start of a new line. The mechanical teletypewriters literally required a “carriage return” (CR) to move the print head back to the beginning of the line, and a “line feed” (LF) to advance the paper roll.

Today, long after mechanical teletypewriters have vanished from our offices, these same invisible characters—Carriage Return (\r or %0d) and Line Feed (\n or %0a)—still govern how our modern internet communicates. The HTTP protocol relies entirely on these CRLF sequences to separate headers from the body of a response.

This reliance on invisible control characters, however, introduced a class of vulnerabilities known as CRLF Injection. When a web framework implicitly trusts user input and places it directly into an HTTP header, it opens the door to a devastating attack called HTTP Response Splitting. This is exactly what occurred in Ruby on Rails prior to version 2.0.5, designated as CVE-2008-5189.

The Anatomy of an HTTP Response

Before we get into the specifics of Rails, though, let’s take a step back and talk about how HTTP works at the protocol level. Decisions and ideas that are central to the idioms of HTTP are also central to understanding why this vulnerability exists.

An HTTP response is fundamentally just a block of plain text. It consists of a status line, one or more headers, a blank line, and finally the response body. That “blank line” is incredibly important; strictly speaking, it is defined by the HTTP specification as two consecutive CRLF sequences (\r\n\r\n).

When a browser or a caching proxy reads an HTTP response from a server, it parses the headers line by line, looking for that double CRLF. The moment it sees \r\n\r\n, it stops parsing headers and treats everything that follows as the body (the HTML, JSON, or image data).

The Vulnerability in redirect_to

In the early days of Rails, it was extremely common to redirect a user back to a previous page after an action, such as logging in. Developers would often pass a URL directly from the request parameters into the redirect_to method:

class SessionsController < ApplicationController
  def create
    # ... authenticate user ...
    
    # Redirect the user back to where they came from
    redirect_to params[:return_to]
  end
end

At first glance, this seems perfectly safe. The redirect_to method simply sets the HTTP status code to 302 Found and adds a Location header pointing to the provided URL.

The problem, though, was that prior to version 2.0.5, Rails did not sanitize the input passed to redirect_to. If an attacker crafted a malicious URL containing encoded CRLF characters (%0d%0a), they could force the server to break out of the Location header and inject entirely new HTTP headers.

For example, an attacker might provide the following return_to parameter:

http://example.com/%0d%0aSet-Cookie:%20evil=true

When Rails generated the HTTP response, it would blindly insert this string into the Location header, resulting in a response that looked like this:

HTTP/1.1 302 Found
Location: http://example.com/
Set-Cookie: evil=true
Content-Type: text/html; charset=utf-8

Because of the injected \r\n (CRLF), the server effectively sent two separate headers to the victim’s browser. The attacker has successfully injected a Set-Cookie header, allowing them to perform session fixation or other cookie-based attacks.

HTTP Response Splitting

Of course, injecting a cookie is just part of the battle. The true danger of CRLF injection is HTTP Response Splitting.

If an attacker injects two consecutive CRLF sequences (%0d%0a%0d%0a), they simulate the end of the HTTP headers entirely. Anything they place after those sequences will be interpreted by the browser—or worse, an intermediary caching proxy—as the response body.

This means an attacker could inject an entirely fake, secondary HTTP response hidden within the headers of the first. By poisoning a shared web cache (like Varnish or a CDN), the attacker could force the cache to save their malicious payload (often containing Cross-Site Scripting scripts) and serve it to subsequent legitimate users.

The Fix in Rails 2.0.5

There are, generally speaking, two major approaches to solving a CRLF injection vulnerability when dealing with redirects.

The first is to strictly validate and whitelist the input. Instead of accepting any URL, the application verifies that the return_to path is relative, belongs to the same domain, and contains no invalid characters. This is often the preferred method for application developers, as it prevents Open Redirect vulnerabilities as well.

The second option, and the one required at the framework level, is to aggressively sanitize the output before setting the HTTP header.

To address CVE-2008-5189, the Rails core team updated the framework to sanitize the destination URL within the redirect_to method. Any newline characters (\r or \n) present in the URL are stripped or encoded before the Location header is constructed, ensuring that the HTTP response remains structurally intact.

Lessons for Modern Applications

While Rails 2.0.5 was released in 2008, the lessons of CVE-2008-5189 remain highly relevant today:

  1. Never Trust User Input: Any data originating from the client, including seemingly harmless redirect URLs, must be treated as potentially malicious.
  2. Understand the Underlying Protocol: Many high-level vulnerabilities are rooted in the low-level text parsing of protocols like HTTP. Understanding how your framework constructs responses is critical to securing it.
  3. Defense in Depth: While modern versions of Rails automatically sanitize headers to prevent CRLF injection, application developers should still validate redirect targets to prevent Open Redirect attacks. Relying on a single layer of security is rarely sufficient.

Today, HTTP response splitting is less common due to stricter parsing by modern web servers, proxies, and frameworks. Nevertheless, though, understanding how simple control characters were weaponized helps us appreciate the robust abstractions that modern Ruby on Rails provides.

Sponsored by Durable Programming

Need help maintaining or upgrading your Ruby on Rails application? Durable Programming specializes in keeping Rails apps secure, performant, and up-to-date.

Hire Durable Programming