Crlf Injection in Gin with Cockroachdb
Crlf Injection in Gin with Cockroachdb — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject a CRLF sequence (\r\n) into a header or query parameter, causing the application to misinterpret boundaries between headers and body or to inject additional headers. In Gin, routes that read user input and forward it into database queries can become conduits for header manipulation if input is not strictly validated before use.
When using Cockroachdb with Gin, a typical pattern is to construct SQL statements or HTTP responses using values from request parameters. If a parameter such as X-User-ID or a query value is reflected into a response header or used to build dynamic SQL without sanitization, a CRLF sequence can split headers and inject arbitrary headers like Set-Cookie or Location. This is especially risky when the application uses the input to build redirect URLs or to log user-specific context into headers that Cockroachdb-backed services then consume.
The Gin framework binds query parameters and path variables into structs or maps, and developers may directly concatenate these into SQL strings or response headers. Cockroachdb, while strongly consistent, does not sanitize inputs; it executes whatever query the application sends. If a CRLF is injected via a parameter such as name=foo%0D%0ASet-Cookie:%20session=attacker, Gin may forward this into a header or into a SQL VALUES clause, causing the HTTP response to include an injected header or causing data to be misinterpreted by downstream services that read from Cockroachdb logs or changefeeds.
In practice, this combination is vulnerable during logging, header construction, or when building dynamic SQL for Cockroachdb. For example, a developer might write a handler that logs a request parameter into a Cockroachdb table for audit purposes. If the parameter contains CRLF, the log entry can span multiple lines, obscuring the boundary between entries and enabling log injection. Additionally, if the application uses the parameter in an HTTP redirect (e.g http://example.com/redirect?url=... ) and does not sanitize url, an attacker can inject headers that Cockroachdb-backed services later interpret as instructions, leading to response splitting or cache poisoning.
Because Gin does not automatically sanitize inputs and Cockroachdb faithfully executes provided SQL, the burden falls on the developer to ensure that any user-controlled data used in headers, SQL, or logging is normalized. Tools like middleBrick can detect Crlf Injection by analyzing Gin endpoints and their interaction with Cockroachdb, identifying places where unsanitized input reaches headers or SQL without proper encoding.
Cockroachdb-Specific Remediation in Gin — concrete code fixes
Remediation focuses on input validation, output encoding, and strict separation of data and control flow. Avoid building SQL by string concatenation; use Cockroachdb prepared statements or parameterized queries. Ensure that any user input used in HTTP headers is stripped of CRLF before being set.
Example of a vulnerable Gin handler that builds SQL dynamically:
// Vulnerable: direct concatenation with user input
func getUser(c *gin.Context) {
userID := c.Query("user_id")
query := fmt.Sprintf("SELECT username FROM users WHERE id = '%s'", userID)
rows, err := db.Query(context.Background(), query)
// handle rows and error
}
Fix using parameterized queries with Cockroachdb:
// Secure: parameterized query with Cockroachdb
func getUserSecure(c *gin.Context) {
userID := c.Query("user_id")
query := "SELECT username FROM users WHERE id = $1"
var username string
err := db.QueryRow(context.Background(), query, userID).Scan(&username)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "server error"})
return
}
c.JSON(http.StatusOK, gin.H{"username": username})
}
When setting headers, explicitly strip or reject CRLF characters:
// Secure: sanitize header values
func setCustomHeader(c *gin.Context) {
raw := c.Query("label")
sanitized := strings.ReplaceAll(raw, "\r", "")
sanitized = strings.ReplaceAll(sanitized, "\n", "")
c.Header("X-Custom-Label", sanitized)
c.JSON(http.StatusOK, gin.H{"ok": true})
}
For logging to Cockroachdb, ensure log entries cannot be split by newlines:
// Secure: sanitize log input before insert
func logAction(c *gin.Context) {
action := c.Query("action")
cleanAction := strings.ReplaceAll(action, "\r", "\\r")
cleanAction = strings.ReplaceAll(cleanAction, "\n", "\\n")
insertQuery := "INSERT INTO audit_log (event) VALUES ($1)"
_, err := db.Exec(context.Background(), insertQuery, cleanAction)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "log failed"})
}
}
Additionally, validate URLs used in redirects to prevent response splitting:
// Secure: validate redirect URL
func safeRedirect(c *gin.Context) {
target := c.Query("url")
if strings.Contains(target, "\r") || strings.Contains(target, "\n") {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid url"})
return
}
// Optionally enforce allowlist or host validation
c.Redirect(http.StatusFound, target)
}
These patterns ensure that Cockroachdb receives clean, parameterized inputs and that Gin responses do not inadvertently split headers or logs due to injected CRLF sequences.