Cors Wildcard in Gin with Api Keys
Cors Wildcard in Gin with Api Keys — how this specific combination creates or exposes the vulnerability
In Gin, using a CORS wildcard with API key authentication creates a security misconfiguration where unauthorized origins can leverage credentials-bearing requests. When app.Use(cors.New(cors.Config{AllowOrigins: []string{"*"}}) is combined with API key validation via middleware such as apikey middleware, the browser may send credentials (cookies, authorization headers) to a wildcard origin if the request includes the API key in a header like X-API-Key. This violates the same-origin policy intent because the wildcard permits any domain to make authenticated requests, effectively exposing the API key–protected endpoint to cross-origin attackers who can trick a victim’s browser into issuing requests on their behalf.
For example, consider a Gin route protected by API key middleware:
gin.Default()
.Use(cors.New(cors.Config{AllowOrigins: []string{"*"}}))
.Use(ApiKeyMiddleware())
.GET("/admin", handler)
An attacker crafts a page on evil.com that loads an image or script from https://api.example.com/admin with the X-API-Key header injected via a preflighted request. If the API key is leaked via Referer or if the endpoint returns sensitive data, the attacker can harvest it. The CORS preflight response with Access-Control-Allow-Origin: * and Access-Control-Allow-Credentials: true is disallowed together in browsers, but many Gin setups use AllowCredentials: true with a wildcard origin, which browsers reject; however, non-browser clients (e.g., compromised servers or misconfigured SDKs) can still make authenticated cross-origin requests, bypassing browser protections and exposing the API key in logs or error messages.
This pattern also interacts poorly with OWASP API Top 10 controls: improper authentication (broken object level authorization when origin is unrestricted) and excessive data exposure (sensitive payloads returned to untrusted origins). A misconfigured wildcard can amplify risks found in other 12 checks, such as input validation flaws that allow header smuggling to spoof the API key origin. Real-world analogues include CVE scenarios where missing origin validation paired with bearer or static key authentication leads to token exfiltration via malicious web pages.
Api Keys-Specific Remediation in Gin — concrete code fixes
Remediate by replacing the CORS wildcard with an allowlist of trusted origins and enforcing strict alignment between CORS credentials settings and API key usage. In Gin, use a named origin list and set AllowCredentials only when necessary, ensuring that API key validation occurs before CORS preflight handling. Below is a secure configuration example:
c := cors.New(cors.Config{
AllowOrigins: []string{"https://trusted.example.com", "https://app.example.com"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowHeaders: []string{"Content-Type", "Authorization", "X-API-Key"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
})
app.Use(c)
app.Use(ApiKeyMiddleware())
app.GET("/admin", handler)
The ApiKeyMiddleware should validate the key from a secure source (e.g., environment variable) and reject requests missing or mismatching the key:
func ApiKeyMiddleware() gin.HandlerFunc {
apiKey := os.Getenv("API_KEY")
return func(c *gin.Context) {
key := c.GetHeader("X-API-Key")
if key != apiKey {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid api key"})
return
}
c.Next()
}
}
If you must support credentialed cross-origin requests, keep AllowCredentials: true but specify exact origins; avoid wildcard origins. For public endpoints that do not require credentials, disable AllowCredentials and keep the wildcard for static assets, but ensure API key–protected routes are under a separate middleware group with stricter CORS rules:
publicGroup := app.Group("/public")
publicGroup.Use(cors.New(cors.Config{
AllowOrigins: []string{"*"},
AllowMethods: []string{"GET"},
AllowHeaders: []string{"Accept"},
AllowCredentials: false,
}))
publicGroup.GET("/health", healthHandler)
secureGroup := app.Group("/secure")
secureGroup.Use(cors.New(cors.Config{
AllowOrigins: []string{"https://trusted.example.com"},
AllowMethods: []string{"POST"},
AllowHeaders: []string{"Content-Type", "X-API-Key"},
AllowCredentials: true,
}))
secureGroup.Use(ApiKeyMiddleware())
secureGroup.GET("/data", dataHandler)
These patterns reduce the attack surface by ensuring that API key validation and CORS policies are aligned, preventing unauthorized cross-origin authenticated requests and mitigating the risk of key leakage via malicious sites.
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 |
Frequently Asked Questions
Is it safe to use AllowCredentials: true with a CORS wildcard in Gin?
Access-Control-Allow-Origin: * and Access-Control-Allow-Credentials: true; however, non-browser clients can still make authenticated cross-origin requests, exposing API keys. Use an explicit allowlist of origins instead.