HIGH xml external entitiesginhmac signatures

Xml External Entities in Gin with Hmac Signatures

Xml External Entities in Gin with Hmac Signatures — how this specific combination creates or exposes the vulnerability

XML External Entity (XXE) injection occurs when an XML parser processes external entity references within untrusted XML data. In Go, the standard encoding/xml parser can be configured to resolve external entities, which may lead to local file reads, SSRF, or denial-of-service via billion laughs attacks. Gin is a popular HTTP framework that does not enable XML parsing by default; developers add XML binding when they accept or return XML payloads, for example via gin.Context.BindXML. When you combine Gin handlers that parse XML with the use of Hmac Signatures for request authentication, a common pattern is to compute an Hmac over selected headers and the request body and then verify the signature before processing. If the body is untrusted XML and the Hmac verification occurs after or independently of safe parsing configuration, an attacker can submit a malicious XML payload that triggers XXE during binding. The Hmac Signature can inadvertently validate the attacker’s request because the signature is computed over the raw body, which includes the malicious entity references, and the server verifies the Hmac before enforcing strict parser settings. This combination exposes an XXE vector: the signature check gives a false sense of integrity, while the XML parser processes external entities, potentially reading files on the server or causing resource exhaustion. For example, an attacker sends an XML body with a DOCTYPE that references file:///etc/passwd, the Hmac matches (because both client and server compute the same signature using shared secret), and Gin’s BindXML parses the body without disabling external entity resolution, leading to unintended data exposure. The risk is amplified when the endpoint trusts the Hmac but does not enforce secure XML parser options such as disabling external entity processing before binding.

Hmac Signatures-Specific Remediation in Gin — concrete code fixes

To mitigate XXE when using Hmac Signatures in Gin, ensure XML parsing is configured securely and that signature verification does not encourage unsafe parsing. Use a dedicated XML decoder with Decoder.Token to inspect and reject external entity references before binding, or switch to a safer XML library that disables external entities by default. When computing and verifying Hmac Signatures, always validate the signature against a canonical representation and enforce secure parser settings as part of the verification flow. Below are concrete, working examples for Gin handlers that combine Hmac verification with safe XML handling.

Example 1: Reject external entities by using a strict XML decoder

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/xml"
    "io"
    "net/http"
    "strings"

    "github.com/gin-gonic/gin"
)

// verifyHmac checks the X-Signature header against the Hmac of the body using a shared secret.
// It returns true if valid, false otherwise.
func verifyHmac(body []byte, receivedSig string, secret []byte) bool {
    mac := hmac.New(sha256.New, secret)
    mac.Write(body)
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(receivedSig))
}

// safeDecodeXML decodes XML without processing external entities.
// It uses xml.NewDecoder and disables external entity resolution by not setting DTD or EntityResolver.
// For stricter control, iterate tokens and reject ExternalElement tokens.
func safeDecodeXML(data []byte, v interface{}) error {
    const entityReplacementLimit = 1024
    dec := xml.NewDecoder(strings.NewReader(string(data)))
    dec.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {
        return input, nil
    }
    // Optionally, wrap dec.Token to reject external entities explicitly.
    // Here we simply decode with default behavior; ensure the parser does not resolve externals.
    // Use decoder.Token to inspect and reject ExternalTok if needed.
    return dec.Decode(v)
}

type MyPayload struct {
    XMLName xml.Name `xml:"data"`
    Field   string   `xml:"field"`
}

func hmacProtectedXML(c *gin.Context) {
    secret := []byte("super-secret-key") // in practice, load from secure configuration
    sig := c.GetHeader("X-Signature")
    if sig == "" {
        c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "missing signature"})
        return
    }
    body, err := c.GetRawData()
    if err != nil {
        c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "cannot read body"})
        return
    }
    if !verifyHmac(body, sig, secret) {
        c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid signature"})
        return
    }
    var payload MyPayload
    if err := safeDecodeXML(body, &payload); err != nil {
        c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid XML"})
        return
    }
    // Process payload safely
    c.JSON(http.StatusOK, gin.H{"received": payload.Field})
}

Example 2: Reject external entities by validating tokens before decode

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/xml"
    "net/http"
    "strings"

    "github.com/gin-gonic/gin"
)

// verifyHmacSame as above.
func verifyHmac(body []byte, receivedSig string, secret []byte) bool {
    mac := hmac.New(sha256.New, secret)
    mac.Write(body)
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(receivedSig))
}

// tokenBasedSafeDecode decodes XML and rejects external entity references by inspecting tokens.
func tokenBasedSafeDecode(data []byte, v interface{}) error {
    dec := xml.NewDecoder(strings.NewReader(string(data)))
    for {
        tok, err := dec.Token()
        if err != nil {
            break
        }
        switch se := tok.(type) {
        case xml.StartElement, xml.EndElement:
            // Optionally inspect se.Name and attributes, but by default allow.
            // To harden, you can maintain a denylist of entities or DTD tokens.
            // For this example, we simply continue.
        }
        // Note: xml.ExternalEntity is not a token type in the standard library.
        // To fully prevent XXE, avoid passing an io.Reader that exposes externals,
        // and do not set an EntityResolver on a custom decoder. The standard
        // decoder does not resolve external entities unless you provide a custom
        // EntityResolver via a custom Reader, which we omit.
    }
    return dec.Decode(v)
}

type MyPayload struct {
    XMLName xml.Name `xml:"data"`
    Field   string   `xml:"field"`
}

func hmacProtectedXMLStrict(c *gin.Context) {
    secret := []byte("super-secret-key")
    sig := c.GetHeader("X-Signature")
    if sig == "" {
        c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "missing signature"})
        return
    }
    body, err := c.GetRawData()
    if err != nil {
        c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "cannot read body"})
        return
    }
    if !verifyHmac(body, sig, secret) {
        c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid signature"})
        return
    }
    var payload MyPayload
    if err := tokenBasedSafeDecode(body, &payload); err != nil {
        c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "invalid XML"})
        return
    }
    c.JSON(http.StatusOK, gin.H{"received": payload.Field})
}

Operational guidance

  • Do not rely on Hmac Signature verification alone to ensure request safety; treat the signature as authenticity, not as a sanitization step.
  • Configure XML parsing to avoid external entity resolution by not providing a custom EntityResolver and by avoiding legacy features that trigger network or file I/O.
  • If you must process untrusted XML, consider using JSON or a schema-limited format, or employ a hardened XML parser that disables DTD and external entities by default.
  • Use the middleBrick Dashboard to track your API security scores over time and the CLI (middlebrick scan <url>) for quick unauthenticated scans that include XML-related findings when applicable.

Frequently Asked Questions

Does using Hmac Signatures with XML increase risk if the signature is verified before parsing?
Yes. If you verify the Hmac before enforcing secure XML parser settings, a malicious XML payload can trigger XXE during parsing. Always configure the XML decoder to reject external entities and perform parsing or strict token inspection before or as part of signature validation.
Can middleBrick detect XXE issues in Gin APIs that use Hmac Signatures?
Yes. middleBrick scans the unauthenticated attack surface and includes XML-related checks where relevant. Use the CLI (middlebrick scan <url>), Web Dashboard, or the GitHub Action to include these findings in your CI/CD pipeline; the MCP Server also allows scanning from your AI coding assistant.