HIGH data exposurechi

Data Exposure in Chi

How Data Exposure Manifests in Chi

Data exposure in Chi APIs typically occurs when sensitive information is inadvertently returned in API responses, error messages, or logs. Chi's middleware-based architecture creates specific patterns where this vulnerability commonly appears.

One frequent scenario involves Chi's context handling. When using c.Get or c.Set with struct values containing sensitive fields, developers might inadvertently expose entire structs in responses:

type User struct {
    ID       string `json:"id"`
    Email    string `json:"email"`
    Password string `json:"password"`
}

func getUser(c *chi.Context) {
    user := User{ID: "123", Email: "[email protected]", Password: "secret123"}
    c.JSON(200, user) // Password exposed!
}

Another Chi-specific pattern involves middleware that captures and returns error details. Chi's error handling middleware can leak stack traces, database queries, or internal paths:

func sensitiveMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                http.Error(w, fmt.Sprintf("Panic: %v", err), 500) // Stack trace exposure
            }
        }()
        next.ServeHTTP(w, r)
    })
}

Route parameter handling in Chi also creates exposure risks. When using c.URLParam or c.Get with user input, sensitive data might be reflected in responses without proper validation:

func echoHandler(c *chi.Context) {
    userID := c.URLParam("id")
    c.JSON(200, map[string]string{"requested_id": userID, "message": "User found"})
}

Chi's chi.URLParam function can also expose internal routing information when error messages include unhandled parameters:

func getUser(c *chi.Context) {
    id := chi.URLParam(c, "id")
    if id == "" {
        c.JSON(400, map[string]string{"error": "Missing ID parameter: " + id})
    }
}

Chi-Specific Detection

Detecting data exposure in Chi applications requires examining both the routing structure and middleware chain. middleBrick's black-box scanning approach is particularly effective for Chi APIs since it tests the actual running service without needing source code access.

When scanning a Chi API, middleBrick examines the response structure systematically. For a Chi endpoint like:

router := chi.NewRouter()
router.Get("/api/users/{id}", getUser)

The scanner tests various ID values to check for information leakage patterns. It looks for:

  • Unexpected fields in JSON responses (like password hashes, API keys, or internal IDs)
  • Stack traces or error details in 500 responses
  • Reflection of sensitive input parameters in error messages
  • Excessive data returned for unauthenticated requests
  • Verbose logging output in responses

middleBrick's Chi-specific detection includes checking for common Chi middleware patterns that might leak data. For example, it identifies when error handling middleware is configured to return detailed error information:

func errorHandler(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                // This would be flagged as data exposure
                http.Error(w, fmt.Sprintf("Error: %v\nStack: %s", err, debug.Stack()), 500)
            }
        }()
        next.ServeHTTP(w, r)
    })
}

The scanner also tests Chi's context propagation by examining how data flows through middleware chains. It checks if sensitive information set in context is properly filtered before responses:

func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx := chi.NewContext(r.Context())
        ctx.Set("user_token", "sensitive_token_value") // This should not appear in responses
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

middleBrick's OpenAPI analysis capability is particularly useful for Chi APIs, as it can compare the documented response schemas against what the actual service returns, identifying discrepancies where sensitive data is exposed beyond the documented contract.

Chi-Specific Remediation

Remediating data exposure in Chi applications involves both architectural changes and careful response filtering. Chi's middleware-based design provides several native approaches to prevent data leakage.

The most effective approach is implementing a response filtering middleware that sanitizes outgoing data. Here's a Chi-specific implementation:

type sensitiveFields map[string]bool

var sensitive = sensitiveFields{
    "password": true,
    "token":   true,
    "secret":  true,
    "key":     true,
    "api_key": true,
}

func sanitizeResponseMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        rw := NewResponseWriter(w)
        next.ServeHTTP(rw, r)
        
        if rw.status >= 200 && rw.status < 400 {
            // Only sanitize successful responses
            var data interface{}
            if err := json.Unmarshal(rw.body, &data); err == nil {
                sanitized := sanitizeData(data)
                json.NewEncoder(w).Encode(sanitized)
            }
        }
    })
}

type ResponseWriter struct {
    http.ResponseWriter
    body   []byte
    status int
}

func NewResponseWriter(w http.ResponseWriter) *ResponseWriter {
    return &ResponseWriter{ResponseWriter: w}
}

func (rw *ResponseWriter) Write(b []byte) (int, error) {
    rw.body = append(rw.body, b...)
    return rw.ResponseWriter.Write(b)
}

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

func sanitizeData(data interface{}) interface{} {
    switch v := data.(type) {
    case map[string]interface{}:
        for key := range v {
            if sensitive[strings.ToLower(key)] {
                v[key] = "[REDACTED]"
            } else {
                v[key] = sanitizeData(v[key])
            }
        }
    case []interface{}:
        for i, item := range v {
            v[i] = sanitizeData(item)
        }
    }
    return data
}

For error handling, Chi's error middleware should be configured to return generic error messages:

func errorHandler(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                log.Printf("Panic: %v\nStack: %s", err, debug.Stack())
                http.Error(w, "Internal server error", 500)
            }
        }()
        next.ServeHTTP(w, r)
    })
}

func main() {
    r := chi.NewRouter()
    r.Use(errorHandler)
    r.Use(sanitizeResponseMiddleware)
    
    r.Get("/api/users/{id}", getUser)
    http.ListenAndServe(":3000", r)
}

Chi's context system should be used carefully to avoid propagating sensitive data. When setting context values, ensure they're properly typed and only accessible where needed:

type contextKey string

const (
    userKey    contextKey = "user"
    authToken  contextKey = "auth_token"
    sensitive  contextKey = "sensitive_data"
)

func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := extractToken(r)
        ctx := r.Context()
        ctx = context.WithValue(ctx, authToken, token)
        
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func getUser(c *chi.Context) {
    token := c.Get(authToken)
    // Use token internally, but never expose it in responses
    user := fetchUserFromDB(token)
    c.JSON(200, user)
}

For structured logging with Chi, ensure sensitive fields are masked before logging:

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

func sensitiveFieldsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Example: mask sensitive query parameters
        q := r.URL.Query()
        if q.Get("password") != "" {
            q.Set("password", "[FILTERED]")
            r.URL.RawQuery = q.Encode()
        }
        next.ServeHTTP(w, r)
    })
}

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

How does Chi's context system contribute to data exposure risks?
Chi's context system allows passing data through middleware chains, but if sensitive information is stored in context without proper scoping, it can be inadvertently exposed in responses. Always use typed context keys and ensure sensitive data is never directly exposed in JSON responses or error messages.
Can middleBrick detect data exposure in Chi APIs without access to the source code?
Yes, middleBrick performs black-box scanning that tests the running Chi API service. It examines actual responses for sensitive data patterns, tests error handling behavior, and analyzes the OpenAPI spec if available to identify discrepancies between documented and actual responses.