Xml External Entities in Gin with Firestore
Xml External Entities in Gin with Firestore — how this specific combination creates or exposes the vulnerability
XML External Entity (XXE) injection occurs when an application processes XML input containing references to external entities without proper restrictions. In a Gin-based Go service that accepts XML payloads and interacts with Google Firestore, this can expose internal resources, lead to server-side request forgery (SSRF), or cause data exfiltration through malicious entity expansions.
Gin does not parse XML by default; developers typically introduce an XML binding library such as encoding/xml or a third-party package to unmarshal request bodies. If the XML unmarshaller is configured to resolve external entities—by not disabling DOCTYPE declarations and external entity processing—attackers can supply crafted XML that references internal file paths, the Firestore metadata service, or other internal endpoints.
Consider a Gin handler that receives an XML report intended to be stored in Firestore. If the XML is decoded with default settings that allow external entity resolution, an attacker can provide:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd" >]> <report> <title>&xxe;</title> <userId>test-user</userId> </report>
When the Gin service decodes this payload, the entity &xxe; expands to the contents of /etc/passwd. If the same handler uses a Firestore client to write the report data without validating or sanitizing fields, the sensitive content may be written to Firestore or used in logging, increasing exposure. Additionally, an attacker can redirect the external entity to the Firestore metadata service at http://metadata.google.internal (reachable from GCP environments) to obtain service account tokens:
<!ENTITY firestore-metadata SYSTEM "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"> <data>&firestore-metadata;</data>
Because Gin routes are often deployed on GCP or in environments where metadata service access is allowed, such requests can leak credentials. Even when Firestore operations are performed server-side, an attacker may chain XXE to SSRF against internal Firestore endpoints if the service account has broad permissions. The combination of a permissive XML parser in Gin and Firestore’s rich API surface increases the impact of insecure defaults.
To detect this class of issue during scanning, middleBrick runs unauthenticated checks that probe XML handling paths and inspect whether external entities are resolved. It does not modify your service but identifies whether responses disclose internal data or trigger requests to internal endpoints, then provides findings mapped to OWASP API Top 10 and compliance frameworks.
Firestore-Specific Remediation in Gin — concrete code fixes
Remediation focuses on preventing external entity resolution at the XML unmarshalling stage and ensuring Firestore interactions treat all input as untrusted. Use strict XML parser configurations and validate/sanitize data before writing to Firestore.
1. Disable external entities in XML unmarshalling
When using encoding/xml, avoid xml.NewDecoder with default settings on untrusted input. Instead, use a decoder with Entity handling restricted or use a secure third-party library that disables DOCTYPE by default. Example of a safer decoder setup:
func safeDecodeXML(data []byte) (*Report, error) {
// Use a restricted reader that limits entity expansion and disables DOCTYPE
const maxEntityMem = 32 * 1024
decoder := xml.NewDecoder(io.LimitReader(bytes.NewReader(data), maxEntityMem))
decoder.Entity = xml.HTMLEntity // minimal built-in entities only; external DTDs are not resolved
var r Report
if err := decoder.Decode(&r); err != nil {
return nil, err
}
return &r, nil
}
Alternatively, use a library such as github.com/clbanning/mxj/v2 which provides map-based parsing without DTD resolution:
func parseWithMXJ(data []byte) (map[string]interface{}, error) {
m, err := mxj.NewMapXml(data)
if err != nil {
return nil, err
}
return m, nil
}
2. Validate and sanitize before Firestore operations
After decoding, validate fields and sanitize content to prevent injection into Firestore. For example, ensure string fields do not contain unexpected markup or entity references:
func storeReport(ctx context.Context, client *firestore.Client, report Report) error {
// Basic validation: ensure no markup-like content in title
if strings.Contains(report.Title, "<") || strings.Contains(report.Title, ">") {
return errors.New("invalid title")
}
_, err := client.Collection("reports").Doc(report.ID).Set(ctx, map[string]interface{}{
"title": report.Title,
"userId": report.UserID,
"created": time.Now(),
})
return err
}
3. Apply least-privilege Firestore rules and service accounts
Even with input validation, configure Firestore security rules and service account permissions to limit write access to expected paths and fields. Example rule snippet:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /reports/{report} {
allow write: if request.auth != null && request.resource.data.keys().hasAll(['title', 'userId'])
&& request.resource.data.title is string && request.resource.data.title.size() < 500;
}
}
}
4. Use middleBrick to verify fixes
After applying these changes, you can use the middleBrick CLI to rescan your endpoint and confirm that XML handling no longer exposes external entities. The CLI provides JSON output suitable for scripting and integrates with the GitHub Action to fail builds if risky XML parsing patterns are detected. For continuous assurance, the Pro plan enables scheduled scans and alerts when new findings appear.
Always test changes in a staging environment and treat all external input as untrusted, regardless of the binding library used.