XML is a data format, but an XML parser can have capabilities that go far beyond reading elements and attributes. Depending on its configuration, a parser may process document type declarations, expand entities, access local files, or make network requests.
Those capabilities can turn a data-processing boundary into a file-access or network-access boundary. An application that accepts XML from an untrusted source must therefore control parser features before parsing begins.
The core rule is simple: for ordinary untrusted XML, disable document type declarations and external entity resolution unless the protocol explicitly requires them and the application has a tightly constrained design for them.
Treat parser features as capabilities
Consider this document:
<?xml version="1.0"?>
<profile>
<name>Ada</name>
</profile>A basic application may only need the profile and name elements. It does not need the parser to contact another host or open a file.
Now consider a document containing a document type declaration:
<?xml version="1.0"?>
<!DOCTYPE profile [
<!ENTITY example SYSTEM "file:///var/app/example.txt">
]>
<profile>
<name>&example;</name>
</profile>If a parser is configured to resolve external entities, the XML document is no longer just supplying data. It is asking the parser to retrieve another resource and insert the result into the parsed document.
The security boundary has changed.
A parser with external access can act with the process’s operating-system and network permissions. If the application process can read a file or connect to an internal service, an unsafe parser configuration may expose that capability to the document sender.
Start with the smallest XML feature set
A secure configuration begins by listing the XML features the application actually needs.
For many APIs and import formats, the required set is small:
- elements;
- attributes;
- text;
- namespaces, when the format uses them.
DTD processing, external general entities, external parameter entities, XInclude, and automatic schema retrieval are separate capabilities. Do not enable them merely because a parser supports them.
This approach is stronger than trying to inspect suspicious entity values after parsing. The dangerous operation may already have happened by the time application code receives the parsed result.
Security policy belongs at parser construction or parser configuration, before untrusted bytes are processed.
Disable both declarations and external resolution
Parser APIs differ across languages and libraries, but two controls are especially important.
First, reject or disable DTD processing when the input format does not require a DTD. This removes a major path for entity declarations and related expansion behavior.
Second, disable access to external resources. This matters even in environments where some DTD behavior remains available for compatibility. A parser should not be able to retrieve arbitrary local or remote resources on behalf of an untrusted document.
Conceptually, the configuration should resemble this:
parser = new XML parser
parser.allow_dtd = false
parser.resolve_external_entities = false
parser.allow_external_network = false
parser.allow_external_files = false
document = parser.parse(untrusted_bytes)These option names are illustrative. Real libraries use different APIs, and defaults can change between parser implementations or versions. Check the documentation for the exact parser in use and add a regression test that proves external access is blocked.
Do not rely on network filtering alone
Outbound network controls are valuable defense in depth, but they are not a substitute for safe parser configuration.
An external entity can target a local file rather than a network service. A network policy may also permit connections to internal systems that the XML sender must not control.
The safer design removes the parser’s external-resolution capability and also restricts the application’s outbound access according to its operational needs.
This gives two independent barriers:
untrusted document
|
v
XML parser cannot resolve external resources
|
v
application has restricted outbound accessIf one control is weakened by a future change, the other still reduces exposure.
Watch for indirect parser creation
Applications often parse XML through a higher-level library rather than constructing a parser directly. Examples include document converters, SOAP clients, SAML tooling, office-file processors, and framework serialization helpers.
The security question remains the same: which parser is eventually processing the bytes, and which features are enabled?
A safe wrapper should make the parser policy explicit. If a library hides parser construction and offers no supported way to disable unsafe external access, treat that limitation as a security design problem rather than assuming the default is safe.
Dependency upgrades deserve the same attention. A change in parser library, wrapper, or configuration path can alter effective behavior even when application code appears unchanged.
Separate validation from parser safety
Schema validation and parser hardening solve different problems.
A schema can constrain the structure of an XML document: required elements, permitted attributes, data types, and cardinality. That is useful for rejecting malformed business data.
It does not automatically make external resource resolution safe.
Apply controls in this order:
bytes from an untrusted source
|
v
hardened parser with external access disabled
|
v
structured XML document
|
v
schema and application validation
|
v
business logicThe parser must be safe before structural validation begins. Then the application can validate the resulting data model according to its own rules.
Bound entity expansion and document size
External entities are not the only XML resource risk. Internal entity expansion can consume large amounts of memory or CPU if a parser permits recursively amplified entity definitions.
If the application does not need DTDs, disabling DTD processing removes this path as well.
Also place ordinary resource limits around XML handling. Useful limits include:
- maximum request or file size;
- maximum parsed document size where supported;
- maximum element depth;
- maximum number of nodes or attributes where supported;
- processing deadlines for expensive transformations.
Resource limits should match the legitimate format. A configuration that accepts documents far larger or deeper than any real use case gives an attacker unnecessary room to consume work.
Be careful with XInclude and schema retrieval
External access can enter through features other than classic entity resolution.
XInclude can instruct a processor to include content from another resource. Schema validation can also trigger retrieval of external schemas or imports in some configurations. Transformation engines may support document-loading functions or related resource access.
Treat each feature as a separate capability.
If validation requires schemas, package approved schemas with the application and resolve them through a controlled local catalog or an equivalent fixed mapping. Do not let an untrusted document choose an arbitrary schema location that the server will fetch.
A strong design converts open-ended resource lookup into a closed mapping:
requested identifier
|
v
approved local catalog
| |
known unknown
| |
local file rejectThis preserves required protocol behavior without turning the parser into a general-purpose fetcher.
Test the security property directly
Configuration review is useful, but a test can prove the behavior that matters.
Create a test document that refers to a resource the parser must never retrieve. The test should pass only when parsing fails safely or leaves the external content unresolved according to the application’s intended behavior.
For example, a test can use a temporary local file containing a unique marker:
marker: xml-parser-test-valueThe test then parses an XML document that attempts to reference that file. The assertion verifies that the marker never appears in the parsed output.
A separate test can point at a local test HTTP listener and assert that the listener receives no request. This checks network resolution without depending on access to any external system.
Keep these tests close to parser initialization. They protect against configuration drift during library upgrades and refactoring.
Avoid insecure compatibility fallbacks
A common operational trap is enabling broad parser features to make one legacy document work.
Suppose a partner feed depends on a DTD. Turning on unrestricted external resolution for every XML document expands the trust granted to all senders.
Prefer a narrow compatibility path:
- identify the exact required feature;
- isolate the parser used for that format;
- map required external identifiers to approved local resources;
- deny all other resource locations;
- apply strict input and processing limits;
- test both accepted and rejected cases.
This keeps exceptional compatibility requirements from becoming global parser policy.
Keep parsing permissions small
Parser hardening works best when the surrounding process also has limited authority.
An XML-processing worker rarely needs access to every application secret, administrative socket, metadata endpoint, or internal service. Isolating document processing in a process or container with narrow file and network permissions reduces the impact of parser defects and future configuration mistakes.
This is especially useful for complex XML workflows involving transformations, signatures, office formats, or third-party libraries. The more components that process the document, the more valuable a small execution boundary becomes.
A practical review checklist
When reviewing an XML ingestion path, verify these points:
- the exact parser implementation is known;
- DTD processing is disabled unless explicitly required;
- external general and parameter entities cannot retrieve resources;
- XInclude is disabled unless explicitly required;
- schema retrieval cannot access arbitrary locations;
- approved schemas or DTDs use a fixed local mapping when needed;
- request size and parser resource use are bounded;
- the processing identity has narrow file and network permissions;
- tests prove that local-file and network retrieval attempts fail;
- dependency upgrades run those tests before release.
The key principle is to treat XML parsing as capability-bearing code. Untrusted XML should describe data, not instruct the application to read files or make network requests. Configure the parser so that distinction is enforced before the first document is processed.