Crlf Injection in Echo Go with Cockroachdb
Crlf Injection in Echo Go 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) sequences into a header or response stream, causing header injection or response splitting. In an Echo Go service that uses Cockroachdb as the backend, this combination can expose or amplify the vulnerability through how HTTP responses are constructed and how data retrieved from Cockroachdb is later rendered.
Echo is a minimalist web framework for Go; if user-controlled input is placed into HTTP response headers without sanitization, an attacker can supply \r\n sequences to inject additional headers or switch to a new response body. Cockroachdb, in this context, is typically used as the data store; if query results or user input stored in Cockroachdb are reflected in HTTP headers (e.g., via X-User-ID or Location), and those values contain newline characters, the injected header can manipulate caching, redirect authentication, or enable HTTP response splitting. This is especially risky when Echo routes construct headers dynamically from Cockroachdb rows without validation or escaping.
For example, consider an Echo route that reads a user profile from Cockroachdb and sets a custom header with the user’s display name:
package main
import (
"github.com/labstack/echo/v4"
"github.com/jackc/pgx/v5/pgxpool"
"net/http"
"strings"
)
func main() {
// Cockroachdb connection pool
pool, _ := pgxpool.New(nil, "postgres://user:pass@host:26257/dbname?sslmode=disable")
e := echo.New()
e.GET("/profile/:username", func(c echo.Context) error {
username := c.Param("username")
var displayName string
// Fetch user data from Cockroachdb
err := pool.QueryRow(c.Request().Context(), "SELECT display_name FROM users WHERE username = $1", username).Scan(&displayName)
if err != nil {
return c.String(http.StatusInternalServerError, "error")
}
// Unsafe: directly setting header with data from Cockroachdb
c.Response().Header().Set("X-Display-Name", displayName)
return c.String(http.StatusOK, "OK")
})
e.Logger.Fatal(e.Start(":8080"))
}
If the display_name stored in Cockroachdb contains characters like \r\nSet-Cookie: auth=evil, the header injection can manipulate downstream responses. Attackers can split the response, inject new headers, or perform cache poisoning. This is a Crlf Injection vector enabled by the Echo Go framework’s direct use of external data and the Cockroachdb backend as a source of untrusted content.
Because this scan tests unauthenticated attack surfaces, an attacker does not need credentials to probe endpoints that reflect Cockroachdb data into headers. The risk is compounded if the application also uses redirects or cookies, where injected headers can alter flow or steal sessions.
Cockroachdb-Specific Remediation in Echo Go — concrete code fixes
Remediation focuses on sanitizing and validating any data from Cockroachdb before it is used in HTTP headers or responses. In Echo Go, you should treat all external data as potentially malicious and remove or encode CR and LF characters.
1. Strip or replace CR/LF characters in header values
Before setting a header with data sourced from Cockroachdb, remove \r and \n. A simple function can normalize strings:
import "strings"
func sanitizeHeaderValue(value string) string {
// Remove carriage return and newline to prevent header injection
value = strings.ReplaceAll(value, "\r", "")
value = strings.ReplaceAll(value, "\n", "")
return value
}
Applied to the earlier example:
func main() {
pool, _ := pgxpool.New(nil, "postgres://user:pass@host:26257/dbname?sslmode=disable")
e := echo.New()
e.GET("/profile/:username", func(c echo.Context) error {
username := c.Param("username")
var displayName string
err := pool.QueryRow(c.Request().Context(), "SELECT display_name FROM users WHERE username = $1", username).Scan(&displayName)
if err != nil {
return c.String(http.StatusInternalServerError, "error")
}
// Safe: sanitize before setting header
safeName := sanitizeHeaderValue(displayName)
c.Response().Header().Set("X-Display-Name", safeName)
return c.String(http.StatusOK, "OK")
})
e.Logger.Fatal(e.Start(":8080"))
}
2. Validate input used in SQL queries and avoid direct reflection of user input into headers
Ensure that any user input used to query Cockroachdb is validated and parameterized (which you already do with $1), but also consider whether the response data needs to be reflected in headers at all. If you must reflect it, enforce allowlists or strict formats (e.g., alphanumeric display names).
func sanitizeHeaderValueStrict(value string) (string, bool) {
// Allow only letters, numbers, spaces, hyphens, underscores
// Reject CR/LF explicitly
if strings.ContainsAny(value, "\r\n") {
return "", false
}
// Optional: regex allowlist
// matched, _ := regexp.MatchString(`^[A-Za-z0-9 _-]+$`, value)
return value, true
}
Use this in your route to reject or replace unsafe values instead of blindly setting headers.
3. Encode output where reflection is unavoidable
If you must include Cockroachdb data in HTTP response bodies (not headers), ensure proper encoding based on context (HTML, JSON, URL). For JSON responses, the standard library’s JSON encoder escapes control characters, but it’s still good practice to validate input length and content.
By combining input validation, header-value sanitization, and strict allowlists, you mitigate Crlf Injection in an Echo Go service backed by Cockroachdb while preserving functionality.