logo

Database

Need

Parsing of untrusted XML with document type declarations and external entities disabled

Context

• Usage of Kotlin 1.9+ on the JVM for building application services

• Usage of the JAXP SAX parser for processing XML documents

Description

1. Non compliant code

import java.io.StringReader
import javax.xml.parsers.SAXParserFactory
import org.xml.sax.InputSource
import org.xml.sax.helpers.DefaultHandler

fun parseOrder(xml: String, handler: DefaultHandler) {
    // The default factory resolves external entities such as file:///etc/passwd
    val parser = SAXParserFactory.newInstance().newSAXParser()...

The `parseOrder` function below parses XML received from clients with a `SAXParser` created from the default `SAXParserFactory`. By default, JAXP parsers process document type declarations and resolve external entities. A document that declares `<!ENTITY xxe SYSTEM "file:///etc/passwd">` and uses `&xxe;` in a field makes the parser read that file and insert it into the parsed data, which the application may then return or store. With `http://` entities, the attacker can make the server send requests to internal hosts, and nested entity definitions ("billion laughs") can exhaust its memory.

2. Steps

• Enable `http://apache.org/xml/features/disallow-doctype-decl` on every `SAXParserFactory`, `DocumentBuilderFactory` and `XMLInputFactory` that parses untrusted data.

• Disable external general and parameter entities and XInclude processing.

• Enable `XMLConstants.FEATURE_SECURE_PROCESSING` to limit entity expansion.

• Configure the factory once and reuse it, so no code path creates an unprotected parser.

• Prefer JSON for data exchanged with clients when XML features are not needed.

3. Secure code example

import java.io.StringReader
import javax.xml.XMLConstants
import javax.xml.parsers.SAXParserFactory
import org.xml.sax.InputSource
import org.xml.sax.helpers.DefaultHandler

private val factory: SAXParserFactory = SAXParserFactory.newInstance().apply {
    // Reject DOCTYPE declarations and never resolve external entities...

The corrected function configures the factory before creating the parser. The `disallow-doctype-decl` feature makes the parser reject any document that contains a `DOCTYPE`, which removes both external entities and entity expansion attacks at once. The external general and parameter entity features are also turned off as a second layer, `isXIncludeAware` stays `false`, and `FEATURE_SECURE_PROCESSING` enables the JDK limits on entity expansion and document size. Documents that legitimately need a DTD are rare in APIs. When one is required, keep `disallow-doctype-decl` off but keep the external entity features disabled, and never resolve entities from untrusted documents.