Xml External Entities in Echo Go with Bearer Tokens
Xml External Entities in Echo Go with Bearer Tokens — how this specific combination creates or exposes the vulnerability
An XML External Entity (XXE) issue in an Echo Go service becomes more sensitive when Bearer tokens are handled in request headers. Echo Go is a lightweight HTTP framework for Go, and like many frameworks it can parse XML payloads if the application explicitly configures an XML decoder or passes raw body data to an XML parser. When an endpoint accepts both XML input and Authorization headers containing Bearer tokens, a developer may inadvertently allow the XML parser to load external resources without restricting entity expansion or external network calls.
In this combination, an attacker can supply a malicious XML body with a DOCTYPE defining an external entity that references a file or network resource, while the Authorization header carries a Bearer token. If the XML parser resolves the external entity, the read file may contain configuration or token material, or the request may be sent to an internal service that trusts the Bearer token extracted from the header. Because the token is often used for downstream service-to-service authorization, leaking it via an external entity or using it to pivot to internal endpoints can expose sensitive data or enable SSRF-style attacks.
For example, an endpoint that decodes JSON into a struct and then re-parses a text field as XML without disabling external entities can be tricked into reading local files. If the Authorization header contains a Bearer token, the token itself is not parsed by the XML decoder, but the context around it matters: the same request that carries the token also carries the malicious XML. A vulnerable Echo Go route might look like an API that accepts profile data and stores it, but if it passes the body to xml.Unmarshal or a custom XML decoder without proper settings, the server may fetch and disclose files via entities like SYSTEM file:///etc/passwd or http://internal-metadata/headers where the token is used.
An attacker might send an XML body with a parameter entity that chains to an external DTD, causing the parser to perform network requests to internal services. Those internal services may trust the Bearer token present in the original request’s Authorization header, effectively allowing the attacker to probe internal APIs through the compromised endpoint. Because Echo Go does not inherently enforce XML security settings, the developer must explicitly disable external entities and DTD processing, and must ensure that Bearer tokens are never reflected in error messages or logs that could be exfiltrated via entity expansions.
Middleware that inspects headers can inadvertently amplify the issue: if logging or tracing captures the Authorization header and includes it in output that is later parsed as XML, sensitive tokens may be stored or exposed. Proper remediation in Echo Go involves disabling external entity resolution at the XML parser level, avoiding passing raw user input to XML parsers, and ensuring that Bearer tokens are handled strictly as opaque strings in headers, never deserialized as part of XML content.
Bearer Tokens-Specific Remediation in Echo Go — concrete code fixes
Secure Echo Go applications by ensuring XML parsing is configured safely and Bearer tokens are treated as opaque strings strictly confined to HTTP headers. Below are concrete, idiomatic code examples for a Go Echo service that avoid XXE while handling Authorization headers correctly.
// Safe XML handling in Echo Go (no external entities)
package main
import (
"encoding/xml"
"net/http"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
type ProfilePayload struct {
Name string `json:"name" xml:"name"`
Email string `json:"email" xml:"email"`
}
// decodeProfileJSONOnly decodes JSON input; XML is not processed.
func decodeProfileJSONOnly(c echo.Context) error {
p := new(ProfilePayload)
if err := c.Bind(p); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid payload"})
}
// Bearer token is read from header and used as an opaque value
auth := c.Request().Header.Get("Authorization")
if auth == "" {
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "missing authorization"})
}
// Validate token format without parsing it as XML
// Example: expect "Bearer "
token, err := validateBearer(auth)
if err != nil {
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "invalid token"})
}
// Use token to call downstream services securely; do not embed in XML
_ = token
return c.JSON(http.StatusOK, map[string]string{"status": "ok"})
}
// validateBearer ensures the header has the expected format.
func validateBearer(auth string) (string, error) {
const prefix = "Bearer "
if len(auth) <= len(prefix) || auth[:len(prefix)] != prefix {
return "", http.ErrAbortHandler
}
token := auth[len(prefix):]
if token == "" {
return "", http.ErrAbortHandler
}
// Optionally validate token structure or audience
return token, nil
}
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// Explicitly disable XML external entities if you must accept XML.
// Use a secure decoder with a restricted parser.
e.POST("/profile", func(c echo.Context) error {
// Prefer JSON; if XML is required, use a custom decoder with these settings:
decoder := xml.NewDecoder(c.Request().Body)
decoder.Entity = xml.HTMLEntity{}
decoder.Strict = true
decoder.CharsetReader = nil
// Ensure no DTD or external entity resolution occurs.
// Do not use decoder.Strict = false with TokenReferences.
var raw map[string]interface{}
if err := decoder.Decode(&raw); err != nil {
return c.JSON(http.StatusBadRequest, map[string]string{"error": "invalid xml"})
}
// Even when parsing XML, avoid reflecting the Authorization header into content
auth := c.Request().Header.Get("Authorization")
if auth == "" {
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "missing authorization"})
}
token, err := validateBearer(auth)
if err != nil {
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "invalid token"})
}
_ = raw
_ = token
return c.NoContent(http.StatusOK)
})
// Start server
e.Logger.Fatal(e.Start(":8080"))
}
Key remediation points specific to the Echo Go + Bearer token context:
- Do not pass raw request bodies to xml.Unmarshal without a secure decoder configuration; prefer JSON parsing for structured data.
- If XML parsing is required, create the decoder with strict settings and avoid exposing the decoder to external DTDs or SYSTEM references.
- Extract Bearer tokens from the Authorization header using a strict prefix check; treat the token as an opaque string and never embed it in XML structures or logs that could be processed by an XML parser.
- Ensure that any logging, metrics, or error reporting does not include the full Authorization header, preventing accidental leakage through error messages that might be parsed as XML.
- Use middleware to validate tokens early and fail closed if the header is malformed, ensuring downstream handlers only receive verified opaque credentials.
By combining secure XML parser settings and disciplined handling of Bearer tokens in headers, Echo Go services can avoid XXE exposures while maintaining proper authorization flows.