Xss Cross Site Scripting in Gin with Api Keys
Xss Cross Site Scripting in Gin with Api Keys — how this specific combination creates or exposes the
Cross-site scripting (XSS) in a Gin API that exposes API keys can occur when user-controlled data is reflected into HTML, JavaScript, or URL contexts without proper encoding, and API keys are returned or embedded in responses. For example, if an endpoint accepts an api_key query parameter and includes its value in the response body without escaping, an attacker can craft a URL like https://api.example.com/search?q=hello&api_key=. When the server reflects the api_key value into the response unescaped, the injected script may execute in the browser of any user who visits that crafted URL. This combination is particularly risky when API keys are used for authorization but are also treated as data, because reflected XSS can expose keys to third parties or enable further attacks such as session hijacking.
In Gin, this typically happens when developers bind query or header parameters directly into templates or JSON responses without validation or escaping. Consider a handler that returns the provided api_key in JSON: c.JSON(200, gin.H{"api_key": apiKey}). If the client-side JavaScript later uses this value in an unsafe way (e.g., inserting it into the DOM via innerHTML), stored XSS can occur. Additionally, if an endpoint echoes the api_key in an HTML context (e.g., during error rendering or debug output), improper context-aware escaping increases XSS risk. Another scenario involves open redirects or dynamic routes where an api_key is part of the path or query string and is reflected without sanitization, enabling injection of malicious payloads that execute in the context of the vulnerable origin.
XSS in this context also intersects with authentication and authorization logic. If an attacker can trick a victim into sending a request that includes a valid API key—perhaps by embedding the key in a malicious page or email—the reflected script can act on behalf of that key. This is especially concerning when keys are used to scope permissions; a compromised key may grant elevated access, and XSS can be used to propagate or exfiltrate that access. Because API keys are often long-lived credentials, exposure through XSS can have longer-term impact compared to session cookies that may be short-lived or rotated more frequently.
Api Keys-Specific Remediation in Gin — concrete code fixes
To mitigate XSS when handling API keys in Gin, treat all external input as untrusted and apply context-specific escaping before use. For JSON responses, use Gin’s built-in JSON rendering correctly and avoid reflecting raw API keys in HTML or JavaScript contexts. If you must return the key to the client, ensure it is treated as an opaque string and never interpreted as executable content. Below are concrete examples demonstrating safe patterns.
Safe JSON response without echoing the key
Do not return the raw API key in JSON unless strictly necessary. If required, ensure it is treated as a plain string and not parsed as code by clients.
// Safe: returning a masked representation instead of the raw key
package main
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/resource", func(c *gin.Context) {
provided := c.Query("api_key")
// Validate format, e.g., expected prefix/length
if !isValidKey(provided) {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid api_key"})
return
}
// Mask most of the key for logging or partial client reference
masked := maskKey(provided)
c.JSON(http.StatusOK, gin.H{
"message": "success",
"api_key": masked, // Never expose full key in JSON
})
})
r.Run()
}
func isValidKey(key string) bool {
// Example validation: alphanumeric, 32 chars
if len(key) != 32 {
return false
}
for _, r := range key {
if !(('a' <= r && r <= 'z') || ('A' <= r && r <= 'Z') || ('0' <= r && r <= '9')) {
return false
}
}
return true
}
func maskKey(key string) string {
if len(key) <= 4 {
return "****"
}
return strings.Repeat("*", len(key)-4) + key[len(key)-4:]
}
Safe error handling and logging
Ensure error messages and logs do not reflect raw API keys in a way that could be captured by XSS sinks. Use structured logging with redaction and avoid injecting user input into HTML templates without escaping.
// Safe: redacting keys in logs and returning generic errors
package main
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/auth", func(c *gin.Context) {
apiKey := c.PostForm("api_key")
// Perform authentication without echoing key in responses
if apiKey == "" || !isValidKey(apiKey) {
// Log safely with redaction
log.Printf("auth failed, api_key=%s", redact(apiKey))
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
return
}
c.JSON(http.StatusOK, gin.H{"status": "ok"})
})
r.Run()
}
func redact(key string) string {
if len(key) > 4 {
return key[:len(key)-4] + "****"
}
return "****"
}
Preventing reflected XSS in HTML contexts
If you render HTML templates, use Gin’s HTML auto-escaping and never inject raw API key values into script or URL contexts.
// Safe: using HTML templates with proper escaping
package main
import (
"html/template"
"net/http"
"github.com/gin-gonic/gin"
)
var tmpl = template.Must(template.New("page").Parse(`
<html>
<body>
<p>Status: {{.Status}}</p>
</body>
</html>
`))
func main() {
r := gin.Default()
r.GET("/debug", func(c *gin.Context) {
apiKey := c.Query("api_key")
if apiKey == "" {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "missing api_key"})
return
}
// Do NOT pass raw apiKey to template; pass safe metadata only
c.HTML(http.StatusOK, "page", gin.H{
"Status": "ok",
})
})
r.LoadHTMLGlob("templates/*")
r.Run()
}
These practices reduce the likelihood that API key handling introduces XSS vectors and align with secure coding patterns for Gin-based services.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |