HIGH crlf injectionchi

Crlf Injection in Chi

How Crlf Injection Manifests in Chi

CRLF injection vulnerabilities in Chi applications occur when untrusted user input containing carriage return ( ) and line feed ( ) characters is incorporated into HTTP headers without proper sanitization. In Chi-based Go applications, this manifests through several common patterns that developers encounter when building middleware or handling request parameters.

The most frequent occurrence happens in custom header manipulation. Consider a Chi application that echoes back client information:

func echoHeader(w http.ResponseWriter, r *http.Request) {
    userAgent := r.URL.Query().Get("ua")
    w.Header().Set("X-Reflected-UA", userAgent)
    w.WriteHeader(http.StatusOK)
}

If an attacker sends ?ua=Firefox Content-Length: 35 You've been pwned!, the response headers become:

HTTP/1.1 200 OK
X-Reflected-UA: Firefox
Content-Length: 35

You've been pwned!

This demonstrates how CRLF characters split the header section from the body, allowing response splitting attacks.

Chi's middleware chain amplifies this risk. When building authentication or logging middleware that captures and reflects request data, developers often inadvertently create injection points:

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Vulnerable: directly using query parameters in headers
        userID := r.URL.Query().Get("user_id")
        w.Header().Set("X-User-ID", userID)
        next.ServeHTTP(w, r)
    })
}

Another Chi-specific manifestation occurs with URL parameter handling. Chi's robust routing allows dynamic parameters that, if reflected in responses, create injection opportunities:

router.Get("/user/{id}/profile", func(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")
    // Vulnerable: id used in header without validation
    w.Header().Set("X-User-ID", id)
    // ... rest of handler
})

The context injection pattern in Chi also presents risks. Developers commonly store user data in request context and later extract it for response headers:

func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Store in context
        ctx := context.WithValue(r.Context(), "user_role", r.URL.Query().Get("role"))
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func profileHandler(w http.ResponseWriter, r *http.Request) {
    role := r.Context().Value("user_role")
    w.Header().Set("X-User-Role", role)
}

Response splitting attacks become particularly dangerous when combined with Chi's middleware architecture, as multiple handlers might contribute to the final response, each potentially introducing CRLF characters.

Chi-Specific Detection

Detecting CRLF injection in Chi applications requires both manual code review and automated scanning. For manual detection, focus on these Chi-specific patterns:

Code Review Checklist:

  • Search for w.Header().Set() calls that use request parameters
  • Identify middleware that reflects request data in response headers
  • Look for chi.URLParam() usage in header construction
  • Check context values being used in response generation
  • Review any custom response writers or header manipulators
  • Examine error responses that might include user input

Automated Scanning with middleBrick:

middleBrick's black-box scanner specifically tests for CRLF injection by submitting payloads containing %0D%0A (URL-encoded CRLF) to various endpoints. The scanner tests:

GET /endpoint?param=Firefox%0D%0AContent-Length:%2035%0D%0A%0D%0AAttackData HTTP/1.1

middleBrick analyzes the response to detect:

  • Additional headers appearing in the response
  • Body content appearing before the expected response body
  • HTTP status code changes
  • Content-Length header manipulation

The scanner provides Chi-specific context by mapping findings to the actual HTTP handlers that processed the request, showing the exact code path where the vulnerability exists.

Testing Individual Endpoints:

# Test a specific Chi endpoint
middlebrick scan https://api.example.com/user/123/profile

The CLI output includes severity levels and the specific header that was vulnerable to injection, along with remediation guidance tailored to Go/Chi patterns.

CI/CD Integration:

Adding middleBrick to your Chi application's CI/CD pipeline ensures CRLF injection vulnerabilities are caught before deployment:

name: API Security Scan
on: [pull_request]
jobs:
  security:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Run middleBrick scan
      run: |
        npm install -g middlebrick
        middlebrick scan https://staging.example.com/api --fail-threshold C

This configuration fails the build if any endpoint receives a C grade or lower, preventing CRLF injection vulnerabilities from reaching production.

Chi-Specific Remediation

Remediating CRLF injection in Chi applications requires input validation and proper header construction. Here are Chi-specific fixes for common vulnerable patterns:

Input Validation Middleware:

func sanitizeHeaders(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Sanitize all query parameters
        r = sanitizeQueryParams(r)
        
        // Sanitize context values
        ctx := sanitizeContextValues(r.Context())
        
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func sanitizeQueryParams(r *http.Request) *http.Request {
    q := r.URL.Query()
    for key := range q {
        q[key] = sanitizeInput(q[key][0])
    }
    r.URL.RawQuery = q.Encode()
    return r
}

func sanitizeInput(input string) string {
    return strings.ReplaceAll(strings.ReplaceAll(input, "\r", ""), "\n", "")
}

Safe Header Construction:

func safeHeaderSet(w http.ResponseWriter, key, value string) {
    sanitizedValue := sanitizeInput(value)
    w.Header().Set(key, sanitizedValue)
}

// Usage in handlers
func userProfile(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")
    safeHeaderSet(w, "X-User-ID", id)
    // ... rest of handler
}

Context Sanitization:

func safeContextValue(ctx context.Context, key string) string {
    value := ctx.Value(key)
    if value == nil {
        return ""
    }
    return sanitizeInput(fmt.Sprintf("%v", value))
}

// In middleware
func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        role := r.URL.Query().Get("role")
        sanitizedRole := sanitizeInput(role)
        ctx := context.WithValue(r.Context(), "user_role", sanitizedRole)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

Comprehensive Input Validation:

func validateInput(input string) error {
    if strings.ContainsAny(input, "\r\n") {
        return fmt.Errorf("input contains forbidden characters")
    }
    // Additional validation rules
    if len(input) > 100 {
        return fmt.Errorf("input too long")
    }
    return nil
}

// Usage in handlers
func createUser(w http.ResponseWriter, r *http.Request) {
    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, "Invalid request", http.StatusBadRequest)
        return
    }
    
    if err := validateInput(user.Name); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    
    // Proceed with safe data
}

Testing Remediation:

func TestCRLFInjection(t *testing.T) {
    // Test that sanitization works
    input := "Firefox\r\nContent-Length: 35\r\n\r\nAttack"
    expected := "FirefoxContent-Length: 35Attack"
    if sanitizeInput(input) != expected {
        t.Errorf("Sanitization failed")
    }
    
    // Test handler with injection attempt
    req := httptest.NewRequest("GET", "/user/123/profile?role=admin%0D%0AX-Injected:Value", nil)
    w := httptest.NewRecorder()
    
    router := chi.NewRouter()
    router.Use(sanitizeHeaders)
    router.Get("/user/{id}/profile", userProfile)
    
    router.ServeHTTP(w, req)
    
    // Verify injected header not present
    if w.Header().Get("X-Injected") != "" {
        t.Errorf("CRLF injection succeeded")
    }
}

These remediation patterns ensure that CRLF injection vulnerabilities are eliminated throughout your Chi application's request processing pipeline.

Frequently Asked Questions

How does CRLF injection differ in Chi vs other Go frameworks?
Chi's middleware architecture and context handling patterns create unique CRLF injection vectors. Unlike simpler frameworks, Chi's robust routing with URL parameters, combined with its context injection system, means vulnerabilities can propagate through multiple layers. The framework's design encourages middleware composition, which can inadvertently create injection points when multiple handlers contribute to response construction. Chi's chi.URLParam() function and context value system require specific sanitization patterns that aren't needed in frameworks with simpler routing.
Can middleBrick detect CRLF injection in Chi applications running on localhost?
Yes, middleBrick can scan localhost endpoints when proper network access is available. For development environments, you can scan http://localhost:3000/api or any local development URL. The scanner works by making HTTP requests to the target endpoint, so as long as the Chi application is running and accessible from the scanning environment, middleBrick can test for CRLF injection vulnerabilities. This is particularly useful for testing before deployment to staging or production environments.