HIGH cross site request forgeryginapi keys

Cross Site Request Forgery in Gin with Api Keys

Cross Site Request Forgery in Gin with Api Keys

Cross Site Request Forgery (CSRF) in a Gin application that uses API keys can arise when API keys are treated as equivalent to session cookies and are automatically included by browsers in requests triggered by third-party sites. In Gin, developers sometimes store an API key in a cookie or in local storage and rely on the browser sending that key with every request to the same origin. If the API key is passed as a custom header (for example, X-API-Key) but the application also accepts GET requests that change state or if CORS is misconfigured, a malicious site can craft requests that the Gin handler processes with the privileges of the victim’s key.

Consider a Gin endpoint that uses an API key for authentication but does not enforce anti-CSRF protections for state-changing methods like POST, PUT, or DELETE. A victim who is authenticated via an API key stored in a cookie might visit a malicious site that issues an image tag or a form submission targeting that endpoint. Because cookies (or headers configured to be sent automatically) are included, the Gin handler may execute the action, interpreting the request as legitimate. This scenario mirrors classic CSRF even though API keys are not designed to be browser-bound; the risk increases if the API key is stored in a long-lived cookie without SameSite attributes or if the endpoint lacks origin checks.

Another vector involves CORS misconfigurations. If a Gin service exposes Access-Control-Allow-Origin with a wildcard and also allows credentials or custom headers, a third-party site can make authenticated requests using the victim’s API key. For instance, an attacker’s JavaScript can read the response of a crafted request if the Gin endpoint returns sensitive data and CORS permits it. Even if the API key is passed as a header, preflight requests may reveal endpoints and headers, and improper validation of the Origin header can allow malicious cross-origin requests to succeed.

Insecure direct object references or authorization issues can compound CSRF risks. If a Gin handler uses an API key to identify a user but does not re-check authorization for the specific resource being modified, an attacker can trick the victim into operating on their own resources—or, in some configurations, on other users’ resources—by forging requests with the victim’s key. This is particularly relevant when API keys are tied to user roles or permissions without additional context checks.

To understand the exposure, examine how API keys are stored and transmitted. Keys stored in cookies without SameSite=Strict or Lax, or without Secure in production, are more likely to be sent automatically by browsers to your Gin endpoints. Keys stored in local storage are not automatically sent, but if your Gin application reads them from local storage and sets them in headers, a Cross-Site Scripting (XSS) flaw could still lead to token theft and facilitate CSRF-like behavior. Therefore, the combination of automatic inclusion by the browser and missing CSRF mitigations creates a tangible attack surface.

CSRF testing within middleBrick’s LLM/AI Security checks includes active prompt injection techniques and system prompt leakage detection, but for API-specific endpoints, it is important to validate that state-changing methods require explicit anti-CSRF controls. Even when using API keys, you should implement per-request nonces, validate the Origin and Referer headers, and use frameworks or middleware that enforce CSRF protection where applicable.

Api Keys-Specific Remediation in Gin

Remediation focuses on ensuring API keys are not automatically sent by browsers for cross-origin requests and that each state-changing request is explicitly authorized. Use strict SameSite cookie attributes, avoid storing sensitive keys in cookies when possible, and require a custom header that cannot be set by third-party sites.

Example 1: API key via custom header with validation. This approach avoids cookie-based automatic inclusion and requires clients to send the key in X-API-Key. The handler explicitly checks the key and does not rely on cookies for authentication.

func APIKeyMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        key := c.GetHeader("X-API-Key")
        if key == "" {
            c.AbortWithStatusJSON(401, gin.H{"error": "missing api key"})
            return
        }
        if !isValidKey(key) {
            c.AbortWithStatusJSON(403, gin.H{"error": "invalid api key"})
            return
        }
        c.Set("api_key", key)
        c.Next()
    }
}

func isValidKey(key string) bool {
    // Compare with constant time to avoid timing attacks
    expected := "YOUR_SECRET_KEY"
    return subtle.ConstantTimeCompare([]byte(key), []byte(expected)) == 1
}

func main() {
    r := gin.Default()
    r.Use(APIKeyMiddleware())
    r.POST("/transfer", func(c *gin.Context) {
        // Business logic here, also re-validate permissions tied to the key
        c.JSON(200, gin.H{"status": "ok"})
    })
    r.Run(":8080")
}

Example 2: If you must use cookies for key storage, set Secure, HttpOnly, and SameSite=Lax or Strict, and avoid using the cookie for automatic authentication of state-changing requests without additional checks.

func SetAPIKeyCookie() gin.HandlerFunc {
    return func(c *gin.Context) {
        cookie := &http.Cookie{
            Name:     "api_key",
            Value:    "YOUR_SECRET_KEY",
            Path:     "/",
            Secure:   true,
            HttpOnly: true,
            SameSite: http.SameSiteStrictMode,
            MaxAge:   3600,
        }
        http.SetCookie(c.Writer, cookie)
        c.Next()
    }
}

func ValidateCookieAPIKey() gin.HandlerFunc {
    return func(c *gin.Context) {
        cookie, err := c.Request.Cookie("api_key")
        if err != nil || cookie.Value != "YOUR_SECRET_KEY" {
            c.AbortWithStatusJSON(401, gin.H{"error": "invalid session"})
            return
        }
        c.Next()
    }
}

Example 3: Require an anti-CSRF token for state-changing requests in addition to API key validation. This pattern is effective when you need stronger guarantees that requests originate from your own frontend.

func CSRPMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        if c.Request.Method == "POST" || c.Request.Method == "PUT" || c.Request.Method == "DELETE" {
            token := c.GetHeader("X-CSRF-Token")
            sessionToken := c.GetCookie("csrf_token")
            if token != sessionToken {
                c.AbortWithStatusJSON(403, gin.H{"error": "invalid CSRF token"})
                return
            }
        }
        c.Next()
    }
}

// In frontend, read a server-provided CSRF token and include it in X-CSRF-Token header
// Ensure cookies for csrf_token have SameSite=Lax/Strict and Secure

Always validate the Origin and Referer headers for sensitive operations, use constant-time comparison for keys, and avoid wildcard CORS that permits arbitrary origins. These measures reduce the likelihood that a forged request from a malicious site will be accepted by your Gin service.

Frequently Asked Questions

Can API keys in cookies lead to CSRF in Gin?
Yes. If API keys are stored in cookies without SameSite=Strict or Lax, browsers will include them in cross-origin requests, enabling CSRF. Use SameSite attributes and prefer custom headers for API key transmission.
Does middleBrick check for CSRF when scanning APIs with API keys?
middleBrick runs security checks including Authentication and BOLA/IDOR. For endpoints using API keys, review findings and apply anti-CSRF controls such as SameSite cookies, custom headers, and origin validation.