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