MEDIUM log injectionchi

Log Injection in Chi

How Log Injection Manifests in Chi

Log injection in Chi applications occurs when untrusted user input flows directly into log statements without proper sanitization. Chi's minimalist router design means developers often implement custom logging middleware or use standard library logging, creating multiple injection vectors.

The most common pattern involves request parameters being logged verbatim. Consider this middleware:

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        log.Printf("Request from %s: %s %s %s", r.RemoteAddr, r.Method, r.URL.Path, r.URL.Query().Encode())
        next.ServeHTTP(w, r)
    })
}

An attacker can inject newline characters and additional log entries by crafting requests like:

GET /api/resource?param=value%0AINFO%20New%20malicious%20entry%0A

This breaks the log structure, creating false entries that appear legitimate. The injection can include timestamps, log levels, and arbitrary content, making forensic analysis unreliable.

Another Chi-specific vector involves path parameters. Since Chi uses named parameters for routing:

router.Get("/user/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")
    log.Printf("Fetching user %s", id)
    // ... handler logic
})

An attacker can inject newlines via URL encoding: /user/123%0AERROR%20Unauthorized%20access%20detected. This creates misleading security alerts in log monitoring systems.

Header injection represents another critical vector. Many Chi applications log request headers for debugging:

log.Printf("Headers: %v", r.Header)

Attackers can craft headers containing newline sequences, breaking log integrity and potentially hiding malicious activity within legitimate log entries.

Structured logging doesn't automatically prevent injection. Consider this JSON logging approach:

logEntry := fmt.Sprintf(`{"timestamp":"%s","method":"%s","path":"%s","query":"%s"}`,
    time.Now().Format(time.RFC3339), r.Method, r.URL.Path, r.URL.RawQuery)
log.Println(logEntry)

If r.URL.RawQuery contains "}","malicious":true,"{", it breaks the JSON structure, potentially causing log parsing failures or creating malformed entries that evade detection.

Chi's context-based request-scoped values create another injection surface. Developers often log these values:

ctx := r.Context()
userID := ctx.Value("user_id").(string)
log.Printf("User %s performed action", userID)

If an attacker manipulates how user_id is stored in context (through middleware vulnerabilities), they can inject arbitrary log content.

Rate limiting and authentication middleware that logs attempts are particularly vulnerable. An attacker can flood logs with injected content, causing log rotation issues, storage exhaustion, or masking actual attacks within the noise.

The impact extends beyond log integrity. Injected log entries can trigger false positives in SIEM systems, cause alert fatigue, or worse, hide actual security incidents within legitimate-looking log entries.

Chi-Specific Detection

Detecting log injection in Chi applications requires both static analysis and runtime monitoring. Start with code review focusing on logging patterns.

Static analysis should identify these anti-patterns:

// Vulnerable: direct interpolation without sanitization
log.Printf("User %s accessed %s", userID, resourceID)

// Vulnerable: logging raw query parameters
log.Printf("Query: %s", r.URL.RawQuery)

// Vulnerable: logging headers without validation
log.Printf("Headers: %v", r.Header)

Automated scanning with middleBrick can detect these patterns by analyzing your Chi application's runtime behavior. middleBrick's black-box scanning tests for log injection by sending requests with encoded newline characters and special characters, then analyzing the actual log output for injection success.

middleBrick specifically tests for:

  • Newline character injection (%0A, %0D)
  • Log level injection (INFO, ERROR, DEBUG prefixes)
  • JSON structure breaking characters
  • Timestamp manipulation attempts
  • Multi-line injection payloads

The scanner examines log files or log aggregation endpoints to verify if injected content appears in the logs, providing a severity score based on injection success and potential impact.

Runtime detection requires monitoring for anomalous log patterns:

func secureLoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Sanitize inputs before logging
        sanitizedMethod := sanitizeLogInput(r.Method)
        sanitizedPath := sanitizeLogInput(r.URL.Path)
        sanitizedQuery := sanitizeLogInput(r.URL.RawQuery)
        
        logEntry := fmt.Sprintf("Request: %s %s?%s from %s",
            sanitizedMethod, sanitizedPath, sanitizedQuery, r.RemoteAddr)
        
        // Detect potential injection attempts
        if containsSuspiciousPatterns(logEntry) {
            logWarn("Potential log injection attempt detected", r)
            http.Error(w, "Invalid request", http.StatusBadRequest)
            return
        }
        
        log.Println(logEntry)
        next.ServeHTTP(w, r)
    })
}

middleBrick's continuous monitoring in Pro tier can alert you when new injection vulnerabilities are introduced in your codebase, comparing current scan results against historical baselines.

Integration testing should include log injection test cases:

func TestLogInjection(t *testing.T) {
    router := chi.NewRouter()
    router.Use(secureLoggingMiddleware)
    
    // Test newline injection
    req := httptest.NewRequest("GET", "/test?param=value%0AERROR%20Injected", nil)
    w := httptest.NewRecorder()
    
    router.ServeHTTP(w, req)
    
    // Verify log output doesn't contain injected content
    logs := getCapturedLogs()
    for _, entry := range logs {
        if strings.Contains(entry, "ERROR Injected") {
            t.Errorf("Log injection successful: %s", entry)
        }
    }
}

Chi-Specific Remediation

Remediation requires a defense-in-depth approach combining input sanitization, structured logging, and secure middleware patterns.

First, implement comprehensive input sanitization for log entries:

func sanitizeLogInput(input string) string {
    // Remove newline characters
    input = strings.ReplaceAll(input, "\n", " ")
    input = strings.ReplaceAll(input, "\r", " ")
    
    // Escape special characters that could break log structure
    input = strings.ReplaceAll(input, "\"", "'")
    input = strings.ReplaceAll(input, "{", "[")
    input = strings.ReplaceAll(input, "}", "]")
    
    // Limit length to prevent log flooding
    if len(input) > 1000 {
        input = input[:997] + "..."
    }
    
    return input
}

// Secure logging middleware
func secureLoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Sanitize all user-controlled inputs
        method := sanitizeLogInput(r.Method)
        path := sanitizeLogInput(r.URL.Path)
        query := sanitizeLogInput(r.URL.RawQuery)
        remoteAddr := sanitizeLogInput(r.RemoteAddr)
        
        // Use structured logging with consistent format
        logEntry := fmt.Sprintf("method=%s path=%s query=%s remote=%s",
            method, path, query, remoteAddr)
        
        log.Println(logEntry)
        next.ServeHTTP(w, r)
    })
}

Implement structured logging with JSON format to prevent injection from breaking log structure:

type logEntry struct {
    Timestamp string `json:"timestamp"`
    Method    string `json:"method"`
    Path      string `json:"path"`
    Query     string `json:"query,omitempty"`
    Remote    string `json:"remote"`
    Status    int    `json:"status"`
}

func jsonLoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Create response wrapper to capture status
        rw := &responseWrapper{ResponseWriter: w, status: http.StatusOK}
        
        // Sanitize inputs
        method := sanitizeLogInput(r.Method)
        path := sanitizeLogInput(r.URL.Path)
        query := sanitizeLogInput(r.URL.RawQuery)
        remote := sanitizeLogInput(r.RemoteAddr)
        
        next.ServeHTTP(rw, r)
        
        // Create structured log entry
        entry := logEntry{
            Timestamp: time.Now().Format(time.RFC3339),
            Method:    method,
            Path:      path,
            Query:     query,
            Remote:    remote,
            Status:    rw.status,
        }
        
        // Marshal to JSON - invalid characters are escaped
        jsonLog, _ := json.Marshal(entry)
        log.Println(string(jsonLog))
    })
}

type responseWrapper struct {
    http.ResponseWriter
    status int
    size   int
}

func (rw *responseWrapper) WriteHeader(code int) {
    rw.status = code
    rw.ResponseWriter.WriteHeader(code)
}

func (rw *responseWrapper) Write(b []byte) (int, error) {
    size, err := rw.ResponseWriter.Write(b)
    rw.size += size
    return size, err
}

For Chi-specific patterns, use the router's built-in parameter handling safely:

router.Get("/user/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")
    
    // Sanitize parameter before logging
    sanitizedID := sanitizeLogInput(id)
    log.Printf("Fetching user: %s", sanitizedID)
    
    // Validate parameter format
    if !isValidUserID(id) {
        http.Error(w, "Invalid user ID", http.StatusBadRequest)
        return
    }
    
    // ... handler logic
})

Implement rate limiting on logging to prevent log flooding attacks:

var logRateLimiter = rate.NewLimiter(rate.Every(time.Minute), 100)

func rateLimitedLoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !logRateLimiter.Allow() {
            // Log rate limit exceeded, but don't include user data
            log.Println("Log rate limit exceeded")
            next.ServeHTTP(w, r)
            return
        }
        
        // Normal logging logic
        next.ServeHTTP(w, r)
    })
}

middleBrick's Pro plan includes continuous monitoring that can alert you when logging patterns change unexpectedly, potentially indicating injection attempts or other anomalies.

Finally, implement log integrity verification:

func verifyLogIntegrity() {
    // Periodically check logs for injection patterns
    logs, err := readRecentLogs()
    if err != nil {
        return
    }
    
    for _, line := range logs {
        if containsInjectionPatterns(line) {
            alertSecurityTeam("Suspected log injection detected")
            break
        }
    }
}

Frequently Asked Questions

How can I test if my Chi application is vulnerable to log injection?
Use middleBrick's black-box scanning by submitting your API URL. The scanner actively tests for log injection by sending requests with encoded newline characters and special characters, then analyzes actual log output to verify if injection succeeds. You can also perform manual testing by sending requests with %0A (newline) characters in parameters and checking your logs for broken structure or injected content.
Does structured logging completely prevent log injection?
No, structured logging reduces but doesn't eliminate the risk. While JSON formatting escapes special characters, attackers can still inject content that appears legitimate within the structured fields. You still need input sanitization, length limits, and validation. middleBrick's scanning tests both structured and unstructured logging patterns to ensure comprehensive coverage.