Crlf Injection in Gin
How CRLF Injection Manifests in Gin
CRLF injection in Gin applications typically occurs when user-controlled input is used in HTTP headers without proper sanitization. The vulnerability arises because HTTP protocol uses CRLF (Carriage Return Line Feed, ) sequences to separate headers. When an attacker can inject these sequences, they can manipulate the HTTP response structure.
In Gin, this often appears in handler functions where request parameters or headers are reflected back in responses. For example:
func vulnerableHandler(c *gin.Context) {
name := c.Query("name")
c.Header("X-User-Name", name)
c.JSON(200, gin.H{
"message": "Hello, " + name,
})
}An attacker could call this endpoint with ?name=John%0D%0ASet-Cookie%3A%20hacked=true, causing the response to include a Set-Cookie header the attacker controls. This demonstrates how CRLF injection can lead to HTTP response splitting.
Gin's middleware chain also presents opportunities for CRLF injection. If middleware logs request data or sets headers based on user input without validation, the entire request processing pipeline becomes vulnerable. The issue is compounded when Gin applications serve as reverse proxies or API gateways, where header manipulation can affect downstream services.
Another Gin-specific manifestation occurs with URL parameter handling. Gin's path parameter binding (c.Param()) can be exploited if the parameter is used in header construction or response generation without proper encoding. The framework's default behavior of passing raw parameter values through the request lifecycle means developers must actively sanitize these inputs.
Gin-Specific Detection
Detecting CRLF injection in Gin applications requires both static code analysis and dynamic testing. Static analysis should focus on identifying all code paths where request data flows to response headers or body content. Look for patterns like:
c.Header(key, value)
c.Set(key, value)
c.Writer.Header().Add(key, value)Dynamic testing involves sending payloads with CRLF sequences to endpoints and observing the response. A simple test using curl:
curl -v "http://localhost:8080/test?name=Test%0D%0AContent-Type%3A%20text/html%0D%0A%0D%0A%3Cscript%3Ealert(1)%3C/script%3E"middleBrick provides automated CRLF injection detection for Gin applications through its black-box scanning approach. The scanner tests each endpoint with various CRLF payloads and analyzes the response structure for anomalies. It specifically looks for:
- Unexpected header additions in the response
- Response body manipulation
- HTTP status code changes
- Content-Type header modifications
The scanner's LLM/AI security module also checks for CRLF injection in AI-related endpoints, testing for system prompt manipulation and jailbreak attempts that might use CRLF sequences to bypass filters.
For comprehensive coverage, middleBrick scans both the OpenAPI specification (if available) and the actual running API. This dual approach catches specification-level issues where endpoint documentation might suggest unsafe parameter handling, as well as runtime vulnerabilities in the actual implementation.
Gin-Specific Remediation
Remediating CRLF injection in Gin requires a defense-in-depth approach. The primary defense is input validation and sanitization. Gin doesn't provide built-in CRLF filtering, so developers must implement their own:
import "regexp"
var crlfPattern = regexp.MustCompile(`\r|\n`)
func sanitizeInput(input string) string {
return crlfPattern.ReplaceAllString(input, "")
}
func secureHandler(c *gin.Context) {
name := sanitizeInput(c.Query("name"))
c.Header("X-User-Name", name)
c.JSON(200, gin.H{
"message": "Hello, " + name,
})
}For header-specific sanitization, create a utility function that validates header names and values against allowed patterns:
func setSecureHeader(c *gin.Context, key, value string) {
if !isValidHeaderKey(key) || !isValidHeaderValue(value) {
c.AbortWithStatusJSON(400, gin.H{"error": "Invalid header"})
return
}
c.Header(key, value)
}
func isValidHeaderKey(key string) bool {
// Only allow alphanumeric and standard header characters
return regexp.MustCompile(`^[a-zA-Z0-9\-]+$`).MatchString(key)
}
func isValidHeaderValue(value string) bool {
// Disallow CRLF and other control characters
return !regexp.MustCompile(`[\x00-\x1F]`).MatchString(value)
}Gin's middleware system provides another layer of protection. Implement a middleware that sanitizes all request parameters before they reach handlers:
func crlfProtectionMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Sanitize query parameters
for key, values := range c.Request.URL.Query() {
for i, value := range values {
c.Request.URL.Query()[key][i] = sanitizeInput(value)
}
}
// Sanitize form data
if c.Request.Method == "POST" || c.Request.Method == "PUT" {
if err := c.Request.ParseForm(); err == nil {
for key := range c.Request.PostForm {
c.Request.PostForm[key] = []string{sanitizeInput(c.Request.PostForm[key][0])}
}
}
}
c.Next()
}
}
// Use in main: r.Use(crlfProtectionMiddleware())For production deployments, combine these code-level protections with middleBrick's continuous monitoring. The Pro plan's scheduled scanning can detect if CRLF protection regressions are introduced during development, providing an additional safety net beyond code reviews.