Cross Site Request Forgery in Gin with Bearer Tokens
Cross Site Request Forgery in Gin with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Cross Site Request Forgery (CSRF) in a Gin API that uses Bearer tokens can occur when token handling and session-like behaviors create implicit trust boundaries an attacker can exploit. Bearer tokens are typically sent in the Authorization header, and while this works well for many client-server and API scenarios, it does not automatically protect against CSRF when the application relies on cookie-based session identifiers for authentication or when tokens are inadvertently exposed to browser contexts.
In Gin, if you use cookie-based sessions or set cookies for authentication alongside Bearer token validation, a malicious site can trigger requests from a victim’s browser that include cookies (such as a session cookie) while the Authorization header with the Bearer token is omitted or not set by the browser. Browsers attach same-origin cookies automatically, but they do not automatically add custom headers like Authorization unless explicitly instructed via CORS or specific client code. However, if the server incorrectly treats the presence of a valid session cookie as sufficient authentication and does not enforce Bearer token validation on critical state-changing endpoints, an attacker can craft a form or script on their site that causes the victim’s browser to perform an unwanted request with valid cookies, leading to unauthorized actions.
A common pattern in Gin is to parse and validate Bearer tokens in middleware for API routes, but if some routes fall back to cookie-based authentication or if middleware is inconsistently applied, the effective authentication boundary becomes fragmented. For example, an endpoint that expects a Bearer token might still process requests when a session cookie is present but ignore the token, creating a CSRF surface. Additionally, CORS misconfigurations can exacerbate this: if the server allows credentials and broad origins, an attacker can leverage the victim’s authenticated session to perform actions while the Authorization header is absent because the browser does not set it for cross-origin requests initiated via forms or scripts.
Consider a Gin handler that performs sensitive operations like changing an email or initiating a fund transfer. If the route relies only on session cookies and does not require a Bearer token, a form on a malicious site can submit a POST to that endpoint, and the browser will include the session cookie, leading to CSRF. Even if Bearer tokens are used, if token validation is skipped for certain HTTP methods or paths, attackers can exploit those gaps. The impact often maps to OWASP API Top 10 categories such as Broken Object Level Authorization (BOLA) when combined with weak authorization checks, and it can lead to unauthorized state changes or data exposure.
To assess this in practice, scans test whether state-changing endpoints require explicit Bearer token validation and whether cookies alone are sufficient. They also examine CORS headers and whether credentials are allowed across origins, because permissive CORS with cookie-based sessions can enable CSRF-style attacks even when Authorization headers are expected. Proper CSRF defense in this context means ensuring that sensitive operations require Bearer tokens, avoiding reliance on cookies for authorization, and aligning CORS policies so that credentials are not broadly allowed with permissive origins.
Bearer Tokens-Specific Remediation in Gin — concrete code fixes
To mitigate CSRF risks when using Bearer tokens in Gin, ensure that authentication is enforced via the Authorization header for all sensitive endpoints and that cookies are not used as the sole authentication mechanism. The following patterns demonstrate how to implement Bearer token validation in middleware and apply it consistently across routes.
First, define a middleware function that extracts and validates the Bearer token. This middleware should reject requests that lack a valid token and set the authenticated principal in the context for downstream handlers.
// authMiddleware validates Bearer tokens and attaches user info to the context.
func authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.AbortWithStatusJSON(401, gin.H{"error": "authorization header missing"})
return
}
const prefix = "Bearer "
if !strings.HasPrefix(authHeader, prefix) {
c.AbortWithStatusJSON(401, gin.H{"error": "invalid authorization header format"})
return
}
token := strings.TrimPrefix(authHeader, prefix)
// Validate token (e.g., JWT verification, introspection, or lookup).
// For this example, assume validateToken returns (userID, error).
userID, err := validateToken(token)
if err != nil {
c.AbortWithStatusJSON(401, gin.H{"error": "invalid token"})
return
}
c.Set("userID", userID)
c.Next()
}
}
// validateToken is a placeholder for your token verification logic.
func validateToken(token string) (string, error) {
// Implement JWT verification, database lookup, or call an auth service.
// Return userID on success, error on failure.
if token == "valid_token_123" {
return "user-123", nil
}
return "", errors.New("invalid token")
}
Apply this middleware to sensitive routes and avoid defining alternate authentication paths that rely solely on cookies. Here is an example of protecting a state-changing endpoint:
func main() {
r := gin.Default()
// Public endpoint: no Bearer token required (or use a different auth scheme if needed).
r.GET("/public", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "public data"})
})
// Protected endpoint: requires Bearer token.
protected := r.Group("/api")
protected.Use(authMiddleware())
{
protected.POST("/transfer", func(c *gin.Context) {
userID, _ := c.Get("userID")
var req struct {
To string `json:"to"`
Amount int `json:"amount"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": "invalid request"})
return
}
// Perform transfer for userID.
c.JSON(200, gin.H{"status": "ok", "user": userID})
})
}
// Listen and serve.
r.Run(":8080")
}
Additionally, avoid storing Bearer tokens in cookies unless they are HttpOnly, Secure, and SameSite=Strict or Lax, and ensure that your CORS configuration does not allow credentials from untrusted origins when cookies are in use. If you must support both token and cookie-based flows, enforce explicit checks that the Authorization header is present for sensitive operations and do not allow cookie-based authentication to bypass token validation. These steps reduce the CSRF surface by ensuring that only requests with valid Bearer tokens can perform privileged actions.