HIGH xss cross site scriptingginapi keys

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 IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Can XSS in Gin APIs expose API keys even if the key is sent as a header?
Yes. If a Gin endpoint reflects header values (e.g., api_key) into an HTML or JavaScript context without escaping, an attacker can inject scripts that capture or misuse the key, regardless of whether it was sent as a header or query parameter.
Does middleBrick detect XSS in Gin APIs that involve API keys?
middleBrick scans unauthenticated attack surfaces and includes input validation checks that can identify reflected XSS patterns. Findings include severity, context, and remediation guidance, but middleBrick detects and reports issues—it does not fix or block them.