Token Leakage in Gin
How Token Leakage Manifests in Gin
Token leakage in Gin applications typically occurs through improper handling of authentication tokens and session data. The most common vulnerability pattern involves inadvertently exposing tokens in HTTP responses, logs, or error messages. In Gin's middleware-based architecture, this often happens when developers chain authentication middleware with error handlers that echo back request data.
A classic example is when authentication middleware captures a token from the Authorization header but then passes it downstream to handlers that might log or display it. Consider this vulnerable pattern:
router := gin.New()
router.Use(gin.Logger())
router.Use(gin.Recovery())
router.GET("/api/protected", func(c *gin.Context) {
token := c.GetHeader("Authorization")
// If authentication fails, the error handler might log the token
if token == "" {
c.JSON(401, gin.H{
"error": "Missing token",
"token_provided": token, // EXPOSES empty token
})
return
}
// ... rest of handler
})
Another Gin-specific manifestation occurs with context binding. When using c.BindJSON() or c.ShouldBind(), if the incoming request contains token fields, they might be automatically bound to struct fields and later exposed in error responses or logs:
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
Token string `json:"token"` // Might be bound from request
}
router.POST("/login", func(c *gin.Context) {
var req LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{
"error": err.Error(),
"received": req, // EXPOSES token if binding partially succeeded
})
return
}
})
Gin's default JSON rendering can also inadvertently expose sensitive fields if struct tags aren't properly configured. A common mistake is omitting -"" tags on sensitive fields:
type UserSession struct {
UserID string
Username string
AccessToken string // NO tag to exclude from JSON
}
router.GET("/session", func(c *gin.Context) {
session := UserSession{
UserID: "123",
Username: "john",
AccessToken: "eyJhbGci...", // WILL be exposed in JSON response
}
c.JSON(200, session)
})
Gin-Specific Detection
Detecting token leakage in Gin applications requires both static analysis and runtime scanning. The most effective approach combines code review for common patterns with automated scanning tools that can identify exposed tokens in responses.
For static detection, look for these Gin-specific patterns:
# Search for token exposure in error responses
grep -r "c.JSON" . | grep -E "(error|400|401|403|500)" | grep -v "-"
# Find structs that might expose sensitive fields
grep -r "json:" . | grep -v "-" | grep -E "(token|secret|key|password)"
# Check middleware chains for token handling
grep -r "Use(" . | grep -E "(auth|token|jwt)"
Runtime detection with middleBrick is particularly effective for Gin applications because it can scan unauthenticated endpoints and identify token leakage without requiring access credentials. The scanner tests for:
- Tokens appearing in HTTP response bodies
- Sensitive data in JSON error responses
- Exposed authentication headers in logs
- Default Gin error pages revealing implementation details
middleBrick's LLM/AI Security module is especially valuable for Gin applications using AI features, as it can detect system prompt leakage and prompt injection vulnerabilities that might expose tokens through AI model interactions.
Here's how to scan a Gin API with middleBrick CLI:
npm install -g middlebrick
middlebrick scan https://your-gin-api.com
# For CI/CD integration
middlebrick scan --threshold B --fail-below https://staging.your-gin-api.com
The scanner tests 12 security categories including Authentication bypass, BOLA/IDOR, and Input Validation—all relevant for detecting token leakage patterns specific to Gin's middleware architecture.
Gin-Specific Remediation
Fixing token leakage in Gin requires a combination of proper struct tagging, middleware design, and response handling. The most effective remediation follows the principle of least privilege—never expose what you don't need to.
First, use proper JSON struct tags to exclude sensitive fields:
type AuthResponse struct {
UserID string `json:"user_id"`
Username string `json:"username"`
AccessToken string `json:"-"` // EXCLUDE from JSON
}
router.POST("/login", func(c *gin.Context) {
// ... authentication logic
token := generateJWT(user)
// Return response without token in JSON
c.JSON(200, gin.H{
"user_id": user.ID,
"username": user.Username,
"message": "Login successful",
})
// Store token in secure HTTP-only cookie instead
c.SetCookie("auth_token", token, 3600, "/", "", true, true)
})
Second, implement proper error handling that doesn't echo back sensitive data:
router.Use(gin.Recovery())
router.GET("/api/protected", func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(401, gin.H{
"error": "Authentication required",
// NEVER include the missing token
})
return
}
// Validate token
claims, err := validateJWT(token)
if err != nil {
c.JSON(401, gin.H{
"error": "Invalid token",
// NEVER include the invalid token
})
return
}
})
Third, use Gin's context binding with care and validate all input:
type SecureRequest struct {
Data string `json:"data"`
// No token field to bind
}
router.POST("/submit", func(c *gin.Context) {
var req SecureRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{
"error": "Invalid request format",
// NEVER include the raw request
})
return
}
// Process request safely
processData(req.Data)
})
For production deployments, integrate middleBrick's continuous monitoring to automatically scan your Gin APIs on a schedule. The Pro plan ($499/mo) includes CI/CD pipeline gates that can fail builds if token leakage is detected, preventing vulnerable code from reaching production.