HIGH xml external entitiesecho gohmac signatures

Xml External Entities in Echo Go with Hmac Signatures

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

An XML External Entity (XXE) attack occurs when an XML parser processes external entity references within untrusted XML data. In Go services built with the Echo framework, developers often parse XML payloads for integrations such as SOAP or legacy file formats. If the XML parser is configured to process external entities and the incoming XML contains malicious entity definitions, the parser can disclose local files, trigger SSRF by making internal requests, or cause denial of service. This becomes critical when Hmac Signatures are used for request authentication and integrity.

Hmac Signatures in Echo are commonly implemented by having clients sign a canonical representation of the request—such as selected headers, a timestamp, and a nonce—using a shared secret. The server recomputes the Hmac and compares it to the value provided in a header (for example, X-Signature). When an attacker can cause the server to parse untrusted XML before Hmac verification, they may embed entity expansions that alter the request body or headers in ways that affect the canonical string. Although Hmac Signatures protect against tampering, they do not prevent XXE if the XML is parsed before signature validation; the server may still process malicious external references, leading to information disclosure or SSRF. In Echo, if middleware parses XML for business logic and then later validates the Hmac, the parsed entities can influence runtime behavior in ways that bypass intended integrity checks, for example by injecting paths or URLs into the parsed structure that later affect logging or error handling.

Consider an endpoint that accepts XML to configure notifications. An attacker can provide an XML body with a DOCTYPE declaring an external file entity such as &ent SYSTEM "file:///etc/passwd". If the Echo handler unmarshals the XML before verifying the Hmac, the parser resolves the entity and reads the file. Even when the Hmac is verified after parsing, the attacker can still trigger side effects during parsing, such as SSRF via an entity that references an internal service endpoint. The combination of XXE-prone XML parsing and Hmac Signatures in Echo can therefore expose the application to security risks if parsing occurs outside the scope of integrity checks or if logs and error messages reflect resolved entity contents.

Hmac Signatures-Specific Remediation in Echo Go — concrete code fixes

Remediation focuses on preventing XML parsing of untrusted data and ensuring that Hmac verification occurs before any processing that could be influenced by external entities. Use secure XML parsing configurations that disable external entities and DTDs. In Go, prefer standard library options that restrict entity expansion, and avoid legacy approaches that enable xml.Decoder with default settings for untrusted input.

Below are concrete, secure code examples for Echo that combine Hmac verification with safe XML handling.

Example 1: Hmac verification before XML parsing

Validate the signature before unmarshaling any XML. This ensures that only authenticated requests proceed to parsing, and you can reject requests with invalid Hmac early.

// computeHmac returns hex-encoded Hmac using SHA256
func computeHmac(payload []byte, secret string) string {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(payload)
    return hex.EncodeToString(mac.Sum(nil))
}

func secureHandler(c echo.Context) error {
    const sharedSecret = "your-secure-shared-secret"
    body, err := ioutil.ReadAll(c.Request().Body)
    if err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, "failed to read body")
    }
    // Restore body for subsequent reads if needed
    reqBody := bytes.NewReader(body)
    c.Request().Body = ioutil.NopCloser(reqBody)

    receivedSig := c.Request().Header.Get("X-Signature")
    if receivedSig == "" {
        return echo.NewHTTPError(http.StatusUnauthorized, "missing signature")
    }
    expectedSig := computeHmac(body, sharedSecret)
    if !hmac.Equal([]byte(expectedSig), []byte(receivedSig)) {
        return echo.NewHTTPError(http.StatusUnauthorized, "invalid signature")
    }

    // Safe XML parsing with disabled external entities
    decoder := xml.NewDecoder(reqBody)
    decoder.Entity = xml.HTMLEntity
    // Use a custom resolver that denies external entities
    decoder.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) {
        return input, nil
    }
    var envelope struct {
        XMLName xml.Name `xml:"Envelope"`
        Body    string   `xml:"Body"`
    }
    if err := decoder.Decode(&envelope); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, "invalid XML")
    }
    // Process envelope.Body safely
    return c.JSON(http.StatusOK, map[string]string{"received": envelope.Body})
}

Example 2: Disable external entities in the XML parser

Use an XML decoder that explicitly disables DTD and external entity resolution to mitigate XXE regardless of processing order.

func safeXMLDecoder(r io.Reader) (*xml.Decoder, error) {
    decoder := xml.NewDecoder(r)
    // Disable DTD and external entities by not setting any resolver that allows them
    // The default token-driven approach with restricted entity handling is secure
    return decoder, nil
}

func handlerWithSecureXML(c echo.Context) error {
    body, err := ioutil.ReadAll(c.Request().Body)
    if err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, "failed to read body")
    }
    reqBody := bytes.NewReader(body)

    // Verify Hmac first
    const sharedSecret = "your-secure-shared-secret"
    receivedSig := c.Request().Header.Get("X-Signature")
    if receivedSig == "" || !hmac.Equal([]byte(computeHmac(body, sharedSecret)), []byte(receivedSig)) {
        return echo.NewHTTPError(http.StatusUnauthorized, "invalid signature")
    }

    decoder, err := safeXMLDecoder(reqBody)
    if err != nil {
        return echo.NewHTTPError(http.StatusInternalServerError, "decoder setup failed")
    }
    // Configure to not resolve external entities (default in many setups, but be explicit)
    // Avoid setting decoder.DTD or any custom resolver that permits external references
    var payload map[string]string
    if err := decoder.Decode(&payload); err != nil {
        return echo.NewHTTPError(http.StatusBadRequest, "invalid XML")
    }
    return c.JSON(http.StatusOK, payload)
}

Operational guidance

  • Always verify Hmac before performing operations that may have side effects, including XML parsing.
  • Disable external entities and DTD processing when parsing untrusted XML; do not rely on parser defaults that may change across Go versions.
  • Treat XML from untrusted sources as high-risk input and apply the same secure handling patterns as you would for JSON or form data.

Frequently Asked Questions

Can Hmac Signatures prevent XXE if parsing happens before verification?
No. Hmac Signatures ensure integrity and authenticity but do not stop an XML parser from processing external entities. If your Echo service parses XML before validating the Hmac, XXE can still occur. Always validate the signature first and use secure XML parsing settings that disable external entities.
What should I do if I must parse XML in Echo with Hmac authentication?
Verify the Hmac before parsing, and configure your XML decoder to disallow external entities and DTDs. Avoid legacy approaches that enable unrestricted entity resolution; prefer strict decoding that does not resolve external references, and treat XML input with the same caution as any untrusted data.