XML Security with xmlFeatures

edit

XML Security with xmlFeatures

Since Lucee 5.4.2 and 6.0, XML parsing is secure by default to protect against XML External Entity (XXE) attacks. DOCTYPE declarations and external entities are blocked out of the box.

You can control XML security settings at two levels:

  • Application-wide via this.xmlFeatures in Application.cfc
  • Per-call by passing a struct as the validator argument to xmlParse(), or as the xmlFeatures argument to isXml()

Per-call settings override the application-level settings for that single operation.

Built-in Feature Keys

Key Type Default Description
secure boolean true Master switch. When true, applies the full OWASP XXE prevention settings: disallows DOCTYPE, disables external entities and parameter entities, disables external DTD loading, disables XInclude, and blocks access to external DTDs and schemas.
disallowDoctypeDecl boolean true When true, any XML containing a <!DOCTYPE> declaration throws an error.
externalGeneralEntities boolean false When true, allows the parser to resolve external entity references (e.g. <!ENTITY xxe SYSTEM "file:///etc/passwd">).
allowExternalEntities boolean false Adobe ColdFusion compatibility alias for externalGeneralEntities. If both are set, their values must match or an error is thrown.

The secure flag is applied first, then disallowDoctypeDecl and externalGeneralEntities override specific features on top. This means you can set secure: true but then selectively relax individual restrictions.

Setting xmlFeatures in Application.cfc

The defaults are secure, so you only need to set this.xmlFeatures if you need to relax the restrictions:

// Application.cfc
component {
	this.name = "myApp";

// these are the defaults, you don't need to set them explicitly this.xmlFeatures = { "secure": true, "disallowDoctypeDecl": true, "externalGeneralEntities": false }; }

To allow DOCTYPE declarations (e.g. for Hibernate mapping files or legacy XML with DTD references):

// Application.cfc
component {
	this.name = "myApp";

this.xmlFeatures = { "secure": false, "disallowDoctypeDecl": false, "externalGeneralEntities": false }; }

You can also update the settings at runtime using application action="update":

application action="update" xmlFeatures = {
	"secure": false,
	"disallowDoctypeDecl": false,
	"externalGeneralEntities": false
};

Per-call Override with xmlParse()

You can override the application-level settings for a single xmlParse() call by passing a struct as the validator argument (the third argument):

// application has secure defaults, but this specific call allows DOCTYPE
xml = xmlParse( xmlString, false, {
	"secure": false,
	"disallowDoctypeDecl": false,
	"externalGeneralEntities": false
} );

This is useful when your application is secure by default but you need to parse a specific document that contains a DOCTYPE declaration.

Per-call Override with isXml()

Similarly, isXml() accepts an xmlFeatures struct as its second argument:

// check if a string is valid XML, allowing DOCTYPE
result = isXml( xmlString, {
	"secure": false,
	"disallowDoctypeDecl": false,
	"externalGeneralEntities": false
} );

Why the Defaults Block DOCTYPE

By default, XML with a DOCTYPE declaration is rejected:

xmlString = '<?xml version="1.0"?>
	<!DOCTYPE foo [
		<!ENTITY xxe SYSTEM "file:///etc/passwd">
	]>
	<foo>&xxe;</foo>';

try { doc = xmlParse( xmlString ); } catch ( e ) { // "DOCTYPE is disallowed when the feature // http://apache.org/xml/features/disallow-doctype-decl set to true" echo( e.message ); }

This is intentional. XXE attacks use DOCTYPE declarations to define external entities that can read local files, make network requests, or cause denial of service. Blocking DOCTYPE at the parser level is the most effective defense.

Pass-through Features

Any keys in the struct that aren't one of the built-in aliases (secure, disallowDoctypeDecl, externalGeneralEntities, allowExternalEntities) are passed directly through to the underlying Java DocumentBuilderFactory.setFeature(). This lets you set any Xerces feature:

this.xmlFeatures = {
	"secure": false,
	"disallowDoctypeDecl": false,
	"externalGeneralEntities": true,
	"http://apache.org/xml/features/validation/id-idref-checking": true
};

You can also use the full Xerces URI form for the built-in features:

// equivalent to disallowDoctypeDecl: true
application action="update" xmlFeatures = {
	"http://apache.org/xml/features/disallow-doctype-decl": true
};

System-level Lock-down

The system property or environment variable lucee.xmlfeatures.override.disable can be set to true to prevent any application-level or per-call overrides. When enabled, XML parsing is locked to the secure defaults and any attempt to override will throw an error.

This is useful for shared hosting environments where you want to enforce XML security across all applications.

-Dlucee.xmlfeatures.override.disable=true

Or as an environment variable:

LUCEE_XMLFEATURES_OVERRIDE_DISABLE=true

What secure: true Does Under the Hood

When secure is true, Lucee configures the Java XML parser following the OWASP XXE Prevention Cheat Sheet:

  • http://apache.org/xml/features/disallow-doctype-decl = true
  • http://xml.org/sax/features/external-general-entities = false
  • http://xml.org/sax/features/external-parameter-entities = false
  • http://apache.org/xml/features/nonvalidating/load-external-dtd = false
  • XInclude aware = false
  • Expand entity references = false
  • Access to external DTD = blocked
  • Access to external schema = blocked

The disallowDoctypeDecl and externalGeneralEntities settings are then applied on top, allowing you to selectively relax specific restrictions while keeping the rest of the secure configuration in place.

See also