Token Leakage in Gin with Cockroachdb
Token Leakage in Gin with Cockroachdb — how this specific combination creates or exposes the vulnerability
Token leakage in a Gin application using CockroachDB typically occurs when authentication tokens, session identifiers, or API keys are inadvertently exposed through logging, error messages, insecure headers, or improper transaction handling. Gin’s minimal framework design places responsibility on the developer to ensure tokens are not embedded in responses or logs, and CockroachDB’s distributed SQL behavior can amplify risks if query results or connection metadata expose sensitive data.
One common pattern is logging full request contexts including headers where authorization tokens are present. For example, using Gin’s default logger with c.Request.Header can print bearer tokens to stdout or a file:
// Risky: logs authorization header containing token
logger.Info("Request headers", gin.H{"headers": c.Request.Header})
If the application uses CockroachDB via a driver like pgx or `cockroachdb` Go driver, tokens might appear in connection strings or debug output. A connection string built from environment variables that include a token can be logged inadvertently:
// Risky: logging database DSN that may contain tokens or credentials
log.Printf("DB connecting to: %s", dsn) // dsn may embed tokens
Error handling is another vector. Gin’s default HTML recovery middleware can return detailed error pages that include stack traces or query snippets. When CockroachDB driver errors surface SQL states or connection details, tokens embedded in query parameters or session contexts may be revealed:
// Risky: exposing query and potential token in error response
rows, err := db.Query("SELECT * FROM sessions WHERE token = $1", token)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()}) // may expose token in error message
}
Insecure HTTP headers compound the issue. If Gin does not strip or restrict headers like Authorization from being reflected in responses, and CockroachDB client libraries propagate metadata in traces or logs, tokens can travel beyond intended boundaries. Cross-service calls where Gin proxies requests to CockroachDB-compatible services may also forward authorization headers without scrubbing, enabling leakage across network boundaries.
Finally, improper use of context in CockroachDB transactions can leave tokens in memory or logs. For instance, storing a token in a request-scoped context and logging the context for debugging purposes may persist the token longer than necessary, increasing exposure risk in distributed CockroachDB logs.
Cockroachdb-Specific Remediation in Gin — concrete code fixes
Remediation focuses on preventing tokens from appearing in logs, error messages, headers, and database interactions. Use structured logging that explicitly excludes sensitive headers, and ensure CockroachDB interactions avoid embedding tokens in queries or connection strings.
1. Configure Gin to skip logging sensitive headers:
// Safe: exclude Authorization header from logs
logger := gin.New()
logger.Use(gin.HandlerFunc(func(c *gin.Context) {
// Copy headers but omit sensitive keys
headers := make(http.Header)
for k, v := range c.Request.Header {
if k != "Authorization" && k != "Cookie" {
headers[k] = v
}
}
c.Set("safeHeaders", headers)
c.Next()
}))
2. Use CockroachDB with parameterized queries and avoid logging tokens or raw queries:
// Safe: parameterized query without logging token
rows, err := db.Query(context.Background(), "SELECT user_id FROM tokens WHERE token = $1", token)
if err != nil {
// Log generic error, not the full error string that may contain token
log.Printf("query failed: %v", err)
c.AbortWithStatusJSON(500, gin.H{"error": "internal error"})
return
}
defer rows.Close()
3. Obfuscate connection strings by loading CockroachDB credentials from secure sources and never printing DSN:
// Safe: build DSN without logging it
host := os.Getenv("COCKROACH_HOST")
port := os.Getenv("COCKROACH_PORT")
user := os.Getenv("COCKROACH_USER")
password := os.Getenv("COCKROACH_PASSWORD")
dbName := os.Getenv("COCKROACH_DB")
dsn := fmt.Sprintf("postgresql://%s:%s@%s:%s/%s?sslmode=require", user, password, host, port, dbName)
// Use dsn without logging
client, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
log.Println("failed to connect to CockroachDB")
return
}
4. Enforce secure HTTP headers and disable reflection of authorization data in responses:
// Safe: remove sensitive headers before response
c.Header("Authorization", "")
c.Header("Cookie", "")
c.Header("Proxy-Authorization", "")
5. Use context values cautiously and avoid storing tokens in request context that may be logged:
// Safe: avoid storing raw tokens in context; store only user ID
ctx := context.WithValue(c.Request.Context(), "userID", userID)
c.Request = c.Request.WithContext(ctx)
// Later, use userID for business logic instead of re-fetching token
6. If using an ORM, configure it to mask sensitive fields in logs and ensure CockroachDB driver logging is disabled in production:
// Example for GORM with CockroachDB: disable gorm logger
sqlDB, _ := db.DB()
sqlDB.SetMaxOpenConns(10)
logMode := logger.Config{LogLevel: logger.Silent}
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{Logger: logMode})