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.