Crlf Injection in Chi with Cockroachdb
Crlf Injection in Chi 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 the header to be split and additional headers or body content to be injected. In a Go application using the Chi router, this typically arises when user-controlled input is reflected into headers such as Location, X-Forwarded-For, or custom headers without proper sanitization. When the application also interacts with Cockroachdb, a distributed SQL database, the injected header content can affect logging, redirection, or session handling that references database identifiers or URLs.
For example, consider a handler that takes a user-supplied returnUrl parameter and sets it in a Location header before redirecting. If the input contains \r\n sequences, an attacker can inject a new header such as Set-Cookie or manipulate the response body after the injected split. Even though Cockroachdb itself does not introduce the injection, an attacker might attempt to exfiltrate or manipulate data by poisoning a redirect that later includes a database primary key or tenant identifier stored in a header or cookie. In a microservice architecture where Chi routes requests to services that query Cockroachdb, an injected header can bypass intended routing or logging logic, potentially exposing sensitive query patterns or configuration details that aid further attacks.
The interaction with Cockroachdb becomes relevant when headers influence how application code builds database queries or connections. For instance, if a header value derived from user input is used to select a database or schema (which is strongly discouraged), CRLF injection could enable header smuggling to an upstream service that then executes unintended SQL against Cockroachdb. While Chi does not parse headers beyond standard Go http.Header handling, the framework’s flexibility with middleware makes it easy to inadvertently pass unsanitized values into downstream handlers that interact with the database. Therefore, the vulnerability is a result of improper input validation on headers in Chi, combined with application logic that references Cockroachdb identifiers or URLs in a way that can be influenced by injected header content.
Cockroachdb-Specific Remediation in Chi — concrete code fixes
Remediation focuses on strict validation and sanitization of user input before it is used in HTTP headers, and avoiding any direct use of external input in database interaction logic. Below are concrete code examples for a Chi router that safely handles user input and interacts with Cockroachdb using the pgx driver.
1. Sanitize header values and avoid reflection of raw user input
Never set headers directly from user input. If a redirect URL is required, validate and normalize it, and ensure it does not contain newline characters. Use a whitelist approach for domains or paths when possible.
import (
"net/http"
"strings"
"github.com/labstack/chi/v5"
)
func safeRedirectHandler(db *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userURL := r.URL.Query().Get("url")
// Reject if contains CR or LF
if strings.ContainsAny(userURL, "\r\n") {
http.Error(w, "invalid parameter", http.StatusBadRequest)
return
}
// Validate URL format and restrict to same host or approved domains
// ... validation logic ...
http.Redirect(w, r, userURL, http.StatusFound)
}
}
2. Use parameterized queries with Cockroachdb to avoid SQL injection and keep data handling separate from headers
When interacting with Cockroachdb, always use prepared statements or parameterized queries. Do not concatenate headers or request metadata into SQL strings.
import (
"database/sql"
"log"
_ "github.com/jackc/pgx/v5/stdlib"
)
func getTenantData(w http.ResponseWriter, r *http.Request) {
tenantID := chi.URLParam(r, "tenantID")
var orgName string
// Use parameterized query to prevent SQL injection
row := db.QueryRow(r.Context(), "SELECT org_name FROM tenants WHERE id = $1", tenantID)
if err := row.Scan(&orgName); err != nil {
if err == sql.ErrNoRows {
http.Error(w, "not found", http.StatusNotFound)
return
}
log.Printf("db query error: %v", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
w.Write([]byte(orgName))
}
3. Apply middleware to reject malicious headers early
Use Chi middleware to inspect and drop headers containing newline characters before they reach business logic that might reference Cockroachdb connection parameters or dynamic header-based routing.
func headerSanityMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
for name, values := range r.Header {
for _, v := range values {
if strings.ContainsAny(v, "\r\n") {
http.Error(w, "malformed header", http.StatusBadRequest)
return
}
}
}
next.ServeHTTP(w, r)
})
}
func main() {
r := chi.NewRouter()
r.Use(headerSanityMiddleware)
r.Get("/data/{tenantID}", getTenantData)
http.ListenAndServe(":8080", r)
}
4. Avoid using headers to influence Cockroachdb connections or queries
Do not derive database connection strings, schema names, or query parameters from HTTP headers that can be influenced by an attacker. If multi-tenancy is required, map tenant identifiers through authenticated session state or URL parameters that are validated against a trusted source, not from raw headers.
// Anti-pattern: using header to select database (do not do this)
func badDBSelection(w http.ResponseWriter, r *http.Request) {
schema := r.Header.Get("X-Tenant-Schema")
// This is unsafe; CRLF injection could manipulate the header to point to another schema or inject SQL-like content
connStr := "postgresql://user:pass@cockroachdb:26257/" + schema
db, err := sql.Open("pgx", connStr)
if err != nil {
http.Error(w, "failed to connect", http.StatusInternalServerError)
return
}
defer db.Close()
}