Crlf Injection in Fiber with Cockroachdb
Crlf Injection in Fiber with Cockroachdb — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject carriage return (CR, \r) and line feed (\n) characters into an HTTP response header, causing header injection or response splitting. In a Fiber application that interacts with Cockroachdb, this typically arises when user-controlled data — such as a query parameter, header, or cookie value — is reflected into HTTP response headers or used to construct dynamic SQL statements without proper sanitization. While Cockroachdb itself does not introduce the injection vector, the way application code reads data from the database and then writes it into headers can create a Crlf Injection opportunity.
Consider a scenario where an endpoint accepts a redirect_url parameter, queries Cockroachdb to validate or enrich the URL, and then sets a Location header for a redirect. If the input is not strictly validated and the database returns a value that contains newline characters, or if the application directly concatenates user input into the header, the injected \r\n can separate headers from the body, enabling HTTP response splitting, header injection, or cache poisoning. Below is a vulnerable Fiber handler that demonstrates this pattern:
// Vulnerable: user input used directly in header after Cockroachdb lookup
app.Get("/redirect", func(c *fiber.Ctx) error {
var target string
// Assume db is a *sql.DB connected to Cockroachdb
row := db.QueryRowContext(c.Context(), "SELECT url FROM redirect_urls WHERE id = $1", c.Query("id"))
if err := row.Scan(&target); err != nil {
return c.Status(fiber.StatusBadRequest).SendString("invalid")
}
// Unsafe: target may contain \r\n from DB or user context
return c.Redirect(target, fiber.StatusFound)
})
Even if the database stores a clean URL, an attacker may control earlier parts of the flow — for example, supplying a malicious id that maps to a crafted entry, or exploiting other input vectors — to introduce newline characters. Because HTTP response splitting relies on injecting sequences that are interpreted as header/body or header/header separators, any reflection of database content into headers must be strictly validated. The same risk exists when setting custom headers based on Cockroachdb results:
// Unsafe: setting a custom header with database content
app.Get("/profile", func(c *fiber.Ctx) error {
var displayName string
row := db.QueryRowContext(c.Context(), "SELECT display_name FROM users WHERE id = $1", c.Params("userID"))
if err := row.Scan(&displayName); err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
c.Set("X-Display-Name", displayName)
return c.SendString("ok")
})
In both examples, if displayName or target contains \r\n, the response can be manipulated. Crlf Injection can lead to HTTP response splitting, enabling cache poisoning, cross-user header injection, or in some configurations, aiding in SSRF or credential leakage. The vulnerability is not in Cockroachdb itself but in how the application handles data retrieved from it before emitting HTTP headers. Mitigation requires canonicalization and strict allow-listing of values used in headers, especially when sourced from external systems like databases.
Cockroachdb-Specific Remediation in Fiber — concrete code fixes
Remediation focuses on two areas: validating data used in HTTP headers and ensuring safe handling of database results. For headers, strip or reject any input containing \r or \n characters. For database interactions, avoid direct concatenation of dynamic values into headers and apply allow-listing where possible. Below are concrete, safe patterns for a Fiber application using Cockroachdb.
Safe redirect with strict validation
Instead of using raw database values in redirects, validate against a strict allow-list (e.g., whitelisted domains) or transform the value to a safe internal route:
// Safe: validate and map to a known route app.Get("/redirect-safe", func(c *fiber.Ctx) error { id := c.Query("id") var path string row := db.QueryRowContext(c.Context(), "SELECT path FROM redirect_map WHERE id = $1", id) if err := row.Scan(&path); err != nil { return c.SendStatus(fiber.StatusBadRequest) } // Ensure path is a safe internal route if path == "" || !strings.HasPrefix(path, "/") { return c.SendStatus(fiber.StatusBadRequest) } return c.Redirect(path, fiber.StatusFound) })Safe custom header with sanitization
Strip CR and LF characters from any database-derived content before setting headers. Use
strings.ReplaceAllor a regex to remove \r and \n, and prefer allow-listing over blacklisting when feasible:// Safe: sanitize header value app.Get("/profile-safe", func(c *fiber.Ctx) error { var displayName string row := db.QueryRowContext(c.Context(), "SELECT display_name FROM users WHERE id = $1", c.Params("userID")) if err := row.Scan(&displayName); err != nil { return c.SendStatus(fiber.StatusInternalServerError) } // Remove CR and LF to prevent header injection safeName := strings.ReplaceAll(displayName, "\r", "") safeName = strings.ReplaceAll(safeName, "\n", "") c.Set("X-Display-Name", safeName) return c.SendString("ok") })Input validation layer
Add a validation middleware that rejects requests containing \r or \n in headers, query parameters, or body fields that may be reflected into headers. This reduces reliance on downstream sanitization:
// Reject raw newlines in critical inputs app.Use(func(c *fiber.Ctx) error { if strings.ContainsAny(c.Request().Header.String(), "\r\n") { return c.SendStatus(fiber.StatusBadRequest) } return c.Next() })These patterns ensure that data from Cockroachdb is treated as untrusted and that headers are constructed safely, preventing Crlf Injection while maintaining the intended functionality of the Fiber application.