Xml External Entities in Gorilla Mux with Firestore
Xml External Entities in Gorilla Mux with Firestore — how this specific combination creates or exposes the vulnerability
An XML External Entity (XXE) attack occurs when an XML processor discloses local files or triggers internal network calls via malicious external entity definitions. Gorilla Mux, a popular HTTP router for Go, often receives raw request bodies that are forwarded to backend services such as Firestore. If a handler parses untrusted XML using an insecure XML decoder that allows external entity processing, the server may read files from the host or interact with internal services. This becomes a combined vulnerability when Firestore client libraries are invoked with data derived from the parsed XML, potentially exposing service account credentials, instance metadata, or internal Firestore rules that assume trusted internal traffic.
Consider a scenario where an API endpoint accepts an XML upload to configure a Firestore document template. Using Go’s standard encoding/xml without disabling external entity resolution allows an attacker to supply an XML payload with a DOCTYPE that references file:///etc/service-account.json or a http://internal-metadata/ URI. If the handler then uses the extracted values to build a Firestore write, the Firestore SDK may propagate the attacker-controlled data into Firestore operations. Because Firestore typically runs inside a trusted environment, internal services might not expect external input, making it easier to pivot or exfiltrate data once a file path is disclosed.
The risk is compounded when the Firestore client is initialized once and reused across requests, as a single successful file read can reveal project IDs, bucket names, or token files that facilitate further attacks. Additionally, if the handler uses Firestore batched writes based on XML-derived fields, an attacker can manipulate references or paths to reach unintended collections. While Firestore itself does not process XML, the interaction between an XXE-vulnerable parser and Firestore operations creates a data exposure path that can violate confidentiality and integrity assumptions.
Firestore-Specific Remediation in Gorilla Mux — concrete code fixes
To secure Gorilla Mux handlers that interact with Firestore, disable external entity processing at the XML parser level and validate/sanitize all inputs before constructing Firestore clients or operations. Below are concrete Go examples demonstrating a vulnerable handler and a remediated version.
Vulnerable Example
The following handler uses the standard XML decoder without restrictions, making it susceptible to XXE:
import (
"encoding/xml"
"net/http"
"cloud.google.com/go/firestore"
)
type Config struct {
Collection string `xml:"collection"`
DocID string `xml:"doc_id"`
Data string `xml:"data"`
}
func vulnerableHandler(w http.ResponseWriter, r *http.Request) {
var cfg Config
// Vulnerable: external entities are resolved
decoder := xml.NewDecoder(r.Body)
if err := decoder.Decode(&cfg); err != nil {
http.Error(w, "invalid XML", http.StatusBadRequest)
return
}
ctx := r.Context()
client, _ := firestore.NewClient(ctx, <project-id>) // simplified
defer client.Close()
_, err := client.Collection(cfg.Collection).Doc(cfg.DocID).Set(ctx, map[string]interface{}{"data": cfg.Data})
if err != nil {
http.Error(w, "firestore error", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
Remediated Example
Use a secure XML decoder that disallows external entities and validate inputs before Firestore operations:
import (
"bytes"
"encoding/xml"
"net/http"
"regexp"
"cloud.google.com/go/firestore"
)
type SafeConfig struct {
Collection string `xml:"collection"`
DocID string `xml:"doc_id"`
Data string `xml:"data"`
}
// restrictedDecoder returns an XMLDecoder that disallows external entities.
func restrictedDecoder(data []byte) (*xml.Decoder, error) {
// Ensure no DOCTYPE is present, a simple but effective mitigation.
if regexp.MustCompile(`<!DOCTYPE`).Match(data) {
return nil, "DOCTYPE not allowed"
}
decoder := xml.NewDecoder(bytes.NewReader(data))
decoder.Entity = xml.HTMLEntity
// Disable external entity resolution by not setting any custom resolver.
return decoder, nil
}
func secureHandler(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "read error", http.StatusBadRequest)
return
}
decoder, err := restrictedDecoder(bodyBytes)
if err != nil {
http.Error(w, "invalid XML", http.StatusBadRequest)
return
}
var cfg SafeConfig
if err := decoder.Decode(&cfg); err != nil {
http.Error(w, "invalid XML", http.StatusBadRequest)
return
}
// Validate collection and document ID to prevent path traversal or injection.
if !regexp.MustCompile(`^[a-zA-Z0-9_-]+$`).MatchString(cfg.Collection) ||
!regexp.MustCompile(`^[a-zA-Z0-9_-]+$`).MatchString(cfg.DocID) {
http.Error(w, "invalid identifier", http.StatusBadRequest)
return
}
ctx := r.Context()
client, _ := firestore.NewClient(ctx, <project-id>)
defer client.Close()
_, err = client.Collection(cfg.Collection).Doc(cfg.DocID).Set(ctx, map[string]interface{}{"data": cfg.Data})
if err != nil {
http.Error(w, "firestore error", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
Additional recommendations:
- Use a dedicated XML schema validation (XSD) or JSON-based input where possible to avoid XML parsing entirely.
- Apply the principle of least privilege to the Firestore service account used by the Gorilla Mux backend, limiting write access to specific collections.
- Integrate middleBrick scans via the CLI (
middlebrick scan <url>) or GitHub Action to detect XXE and related API security issues as part of your CI/CD pipeline.