From eda0f7ad7cafa4156406215cb40f5133f61ec23b Mon Sep 17 00:00:00 2001 From: Ben McIlwain Date: Tue, 30 Jun 2026 14:19:50 -0400 Subject: [PATCH] Harden EppXmlSanitizer against XXE and entity expansion (#3117) Configure a throwing XMLResolver on XMLInputFactory in XmlTransformer to act as an additional defense-in-depth security layer against XML External Entity (XXE) and entity expansion/resolution attacks. This prevents resolution of external and internal entities in StAX parsing pipelines, ensuring that malformed payloads containing entity definitions are safely blocked and rejected. Also add a unit test to EppXmlSanitizerTest verifying that EPP messages containing DTD and external entities fail parsing and fall back safely to their Base64 representation. TAG=agy CONV=610c2358-a99f-4605-94cd-ff0d4ee08176 BUG= http://b/529387728 --- .../main/java/google/registry/xml/XmlTransformer.java | 4 ++++ .../java/google/registry/flows/EppXmlSanitizerTest.java | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/core/src/main/java/google/registry/xml/XmlTransformer.java b/core/src/main/java/google/registry/xml/XmlTransformer.java index d290ea278..d422d76e8 100644 --- a/core/src/main/java/google/registry/xml/XmlTransformer.java +++ b/core/src/main/java/google/registry/xml/XmlTransformer.java @@ -115,6 +115,10 @@ public class XmlTransformer { // Prevent XXE attacks. xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); + xmlInputFactory.setXMLResolver( + (publicID, systemID, baseURI, namespace) -> { + throw new XMLStreamException("Entity resolution disabled."); + }); return xmlInputFactory; } diff --git a/core/src/test/java/google/registry/flows/EppXmlSanitizerTest.java b/core/src/test/java/google/registry/flows/EppXmlSanitizerTest.java index d7598a609..b27d6118e 100644 --- a/core/src/test/java/google/registry/flows/EppXmlSanitizerTest.java +++ b/core/src/test/java/google/registry/flows/EppXmlSanitizerTest.java @@ -116,4 +116,13 @@ class EppXmlSanitizerTest { String sanitizedXml = sanitizeEppXml(inputXml.getBytes(UTF_16LE)); assertThat(sanitizedXml).isEqualTo(inputXml); } + + @Test + void testSanitize_withDtd_returnsBase64() { + String inputXml = "]>&xxe;"; + byte[] inputXmlBytes = inputXml.getBytes(UTF_8); + // Since DTDs are disabled, parsing should fail and fallback to base64 encoding of input. + String expectedBase64 = Base64.getMimeEncoder().encodeToString(inputXmlBytes); + assertThat(sanitizeEppXml(inputXmlBytes).trim()).isEqualTo(expectedBase64.trim()); + } }