Skip to content
šŸ”µInfo

Java XXE Prevention

Comprehensive guide to preventing XXE vulnerabilities in Java applications using secure XML parser configurations across different libraries.

Overview

Java applications are particularly susceptible to XXE attacks because many XML parsers have external entity processing enabled by default. This guide covers secure configuration for the most common Java XML parsing libraries:

  • DocumentBuilderFactory (DOM)
  • SAXParserFactory (SAX)
  • XMLInputFactory (StAX)
  • TransformerFactory (XSLT)
  • SchemaFactory
  • XMLReader

The key principle: Explicitly disable all dangerous features before parsing any untrusted XML.

DocumentBuilderFactory (DOM Parsing)

DocumentBuilderFactory is the most common way to parse XML in Java. Here's the secure configuration:

Secure DocumentBuilderFactory

JavaSecureDocumentBuilder.javaāœ“ Secure
1import javax.xml.parsers.DocumentBuilder;
2import javax.xml.parsers.DocumentBuilderFactory;
3import javax.xml.parsers.ParserConfigurationException;
4
5public class SecureDocumentBuilder {
6    public static DocumentBuilder createSecureBuilder() 
7            throws ParserConfigurationException {
8        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
9        
10        // This is the PRIMARY defense. If DTDs (doctypes) are disallowed,
11        // almost all XXE attacks are prevented
12        String FEATURE = "http://apache.org/xml/features/disallow-doctype-decl";
13        dbf.setFeature(FEATURE, true);
14        
15        // If you can't completely disable DOCTYPE, then at least do these:
16        // Disable external general entities
17        dbf.setFeature(
18            "http://xml.org/sax/features/external-general-entities", 
19            false
20        );
21        
22        // Disable external parameter entities
23        dbf.setFeature(
24            "http://xml.org/sax/features/external-parameter-entities", 
25            false
26        );
27        
28        // Disable external DTDs
29        dbf.setFeature(
30            "http://apache.org/xml/features/nonvalidating/load-external-dtd",
31            false
32        );
33        
34        // Disable XInclude processing
35        dbf.setXIncludeAware(false);
36        
37        // Disable entity expansion
38        dbf.setExpandEntityReferences(false);
39        
40        return dbf.newDocumentBuilder();
41    }
42}

SAXParserFactory (SAX Parsing)

SAX parsers are event-driven and also need secure configuration:

Secure SAXParserFactory

JavaSecureSAXParser.javaāœ“ Secure
1import javax.xml.parsers.SAXParser;
2import javax.xml.parsers.SAXParserFactory;
3import javax.xml.parsers.ParserConfigurationException;
4import org.xml.sax.SAXException;
5
6public class SecureSAXParser {
7    public static SAXParser createSecureParser() 
8            throws ParserConfigurationException, SAXException {
9        SAXParserFactory spf = SAXParserFactory.newInstance();
10        
11        // Disable DOCTYPE declarations
12        spf.setFeature(
13            "http://apache.org/xml/features/disallow-doctype-decl", 
14            true
15        );
16        
17        // Disable external general entities
18        spf.setFeature(
19            "http://xml.org/sax/features/external-general-entities", 
20            false
21        );
22        
23        // Disable external parameter entities
24        spf.setFeature(
25            "http://xml.org/sax/features/external-parameter-entities", 
26            false
27        );
28        
29        // Disable loading external DTD
30        spf.setFeature(
31            "http://apache.org/xml/features/nonvalidating/load-external-dtd", 
32            false
33        );
34        
35        return spf.newSAXParser();
36    }
37}

XMLInputFactory (StAX Parsing)

StAX (Streaming API for XML) also requires secure configuration:

Secure XMLInputFactory

JavaSecureStAXParser.javaāœ“ Secure
1import javax.xml.stream.XMLInputFactory;
2import javax.xml.stream.XMLStreamException;
3
4public class SecureStAXParser {
5    public static XMLInputFactory createSecureFactory() {
6        XMLInputFactory xif = XMLInputFactory.newFactory();
7        
8        // Disable DTDs entirely
9        xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
10        
11        // Disable external entities
12        xif.setProperty(
13            XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, 
14            false
15        );
16        
17        return xif;
18    }
19}

TransformerFactory (XSLT)

XSLT transformers can also be exploited for XXE attacks:

Secure TransformerFactory

JavaSecureTransformer.javaāœ“ Secure
1import javax.xml.transform.TransformerFactory;
2import javax.xml.transform.TransformerConfigurationException;
3import javax.xml.XMLConstants;
4
5public class SecureTransformer {
6    public static TransformerFactory createSecureFactory() 
7            throws TransformerConfigurationException {
8        TransformerFactory tf = TransformerFactory.newInstance();
9        
10        // Disable access to external DTDs and stylesheets
11        tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
12        tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
13        
14        // For older Java versions, use:
15        tf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
16        
17        return tf;
18    }
19}

SchemaFactory (XML Schema)

Schema validation can also be vulnerable:

Secure SchemaFactory

JavaSecureSchema.javaāœ“ Secure
1import javax.xml.validation.SchemaFactory;
2import javax.xml.XMLConstants;
3import org.xml.sax.SAXException;
4
5public class SecureSchema {
6    public static SchemaFactory createSecureFactory() 
7            throws SAXException {
8        SchemaFactory sf = SchemaFactory.newInstance(
9            XMLConstants.W3C_XML_SCHEMA_NS_URI
10        );
11        
12        // Disable access to external DTDs and schemas
13        sf.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
14        sf.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
15        
16        return sf;
17    }
18}

XMLReader

XMLReader also needs secure configuration:

Secure XMLReader

JavaSecureXMLReader.javaāœ“ Secure
1import org.xml.sax.XMLReader;
2import org.xml.sax.SAXException;
3import javax.xml.parsers.SAXParserFactory;
4import javax.xml.parsers.ParserConfigurationException;
5
6public class SecureXMLReader {
7    public static XMLReader createSecureReader() 
8            throws SAXException, ParserConfigurationException {
9        SAXParserFactory spf = SAXParserFactory.newInstance();
10        
11        spf.setFeature(
12            "http://apache.org/xml/features/disallow-doctype-decl", 
13            true
14        );
15        spf.setFeature(
16            "http://xml.org/sax/features/external-general-entities", 
17            false
18        );
19        spf.setFeature(
20            "http://xml.org/sax/features/external-parameter-entities", 
21            false
22        );
23        
24        XMLReader reader = spf.newSAXParser().getXMLReader();
25        return reader;
26    }
27}

Complete Secure Example

Here's a complete example showing secure XML parsing in a real application:

Production-Ready Secure Parser

JavaProductionXMLParser.javaāœ“ Secure
1import javax.xml.parsers.DocumentBuilder;
2import javax.xml.parsers.DocumentBuilderFactory;
3import org.w3c.dom.Document;
4import java.io.InputStream;
5import java.util.logging.Logger;
6
7public class ProductionXMLParser {
8    private static final Logger LOGGER = 
9        Logger.getLogger(ProductionXMLParser.class.getName());
10    
11    private final DocumentBuilder builder;
12    
13    public ProductionXMLParser() throws Exception {
14        this.builder = createSecureBuilder();
15    }
16    
17    private DocumentBuilder createSecureBuilder() throws Exception {
18        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
19        
20        try {
21            // PRIMARY DEFENSE: Disable DOCTYPE
22            dbf.setFeature(
23                "http://apache.org/xml/features/disallow-doctype-decl", 
24                true
25            );
26            
27            // SECONDARY DEFENSES (in case DOCTYPE can't be disabled)
28            dbf.setFeature(
29                "http://xml.org/sax/features/external-general-entities", 
30                false
31            );
32            dbf.setFeature(
33                "http://xml.org/sax/features/external-parameter-entities", 
34                false
35            );
36            dbf.setFeature(
37                "http://apache.org/xml/features/nonvalidating/load-external-dtd",
38                false
39            );
40            
41            // Additional security settings
42            dbf.setXIncludeAware(false);
43            dbf.setExpandEntityReferences(false);
44            
45            LOGGER.info("Secure XML parser initialized successfully");
46            
47        } catch (Exception e) {
48            LOGGER.severe("Failed to configure secure XML parser: " 
49                + e.getMessage());
50            throw new Exception(
51                "Could not create secure XML parser", e
52            );
53        }
54        
55        return dbf.newDocumentBuilder();
56    }
57    
58    public Document parseXML(InputStream xmlInput) throws Exception {
59        try {
60            return builder.parse(xmlInput);
61        } catch (Exception e) {
62            LOGGER.severe("XML parsing failed: " + e.getMessage());
63            throw new Exception("Invalid XML input", e);
64        }
65    }
66}

Testing Your Configuration

Always test that your configuration actually prevents XXE attacks:

Unit Test for XXE Prevention

JavaXMLParserTest.java
1import org.junit.Test;
2import static org.junit.Assert.*;
3
4public class XMLParserTest {
5    
6    @Test(expected = Exception.class)
7    public void testXXEIsBlocked() throws Exception {
8        // This XXE payload should be rejected
9        String xxePayload = 
10            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
11            "<!DOCTYPE foo [" +
12            "  <!ENTITY xxe SYSTEM \"file:///etc/passwd\">" +
13            "]>" +
14            "<root>" +
15            "  <data>&xxe;</data>" +
16            "</root>";
17        
18        ProductionXMLParser parser = new ProductionXMLParser();
19        
20        // This should throw an exception due to DOCTYPE
21        parser.parseXML(
22            new ByteArrayInputStream(xxePayload.getBytes())
23        );
24        
25        fail("XXE payload was not blocked!");
26    }
27    
28    @Test
29    public void testValidXMLIsParsed() throws Exception {
30        String validXml = 
31            "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
32            "<root>" +
33            "  <data>Hello World</data>" +
34            "</root>";
35        
36        ProductionXMLParser parser = new ProductionXMLParser();
37        Document doc = parser.parseXML(
38            new ByteArrayInputStream(validXml.getBytes())
39        );
40        
41        assertNotNull(doc);
42    }
43}

Common Mistakes to Avoid

Mistake 1: Partial Configuration āŒ Only disabling one feature āœ“ Disable ALL dangerous features

Mistake 2: Wrong Feature Names āŒ Using incorrect feature URIs āœ“ Copy exact feature names from this guide

Mistake 3: Not Testing āŒ Assuming configuration works without testing āœ“ Write unit tests with XXE payloads

Mistake 4: Catching and Ignoring Exceptions āŒ try { setFeature(...) } catch (Exception e) {} āœ“ Let configuration failures propagate

Mistake 5: Using Vulnerable Libraries āŒ Using outdated XML libraries āœ“ Keep dependencies up to date

Quick Reference Summary

For DocumentBuilderFactory:

dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

For SAXParserFactory:

spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

For XMLInputFactory:

xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);

For TransformerFactory:

tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
tf.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");

Remember: The single most effective defense is disallow-doctype-decl.