Skip to content

Commit 9bf87fb

Browse files
committed
update xmlFeatures docs
1 parent d0446ae commit 9bf87fb

5 files changed

Lines changed: 246 additions & 12 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
A struct of xmlFeatures directives to override defaults
1+
A struct of `xmlFeatures` directives to override the application-level XML security settings for this single call. See the [XML Security with xmlFeatures](/docs/recipes/xml-security-xmlfeatures) recipe for available keys and examples.

docs/03.reference/01.functions/xmlparse/_arguments/validator.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ Any of the following:
33
- A string containing a DTD or Schema
44
- The name of a DTD or Schema file
55
- The URL of a DTD or Schema file; valid protocol identifiers include http, https, ftp, and file
6-
- A struct of xmlFeatures directives - since 5.4.2.20
6+
- A struct of `xmlFeatures` directives (since 5.4.2.20) — overrides the application-level XML security settings for this single parse call. See the usage notes below for available keys.
Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,46 @@
1+
**Basic Usage**
2+
13
```luceescript+trycf
2-
xml_stream = "
3-
<note>
4-
<to>Alice</to>
5-
<from>Bob</from>
6-
<heading>Reminder</heading>
7-
<body>Here is the message you requested.</body>
8-
</note>
9-
";
10-
11-
dump(XmlParse(xml_stream));
4+
xml_stream = "
5+
<note>
6+
<to>Alice</to>
7+
<from>Bob</from>
8+
<heading>Reminder</heading>
9+
<body>Here is the message you requested.</body>
10+
</note>
11+
";
12+
13+
dump( XmlParse( xml_stream ) );
1214
```
15+
16+
**Parsing XML with DOCTYPE (xmlFeatures override)**
17+
18+
By default, XML containing a DOCTYPE declaration is blocked. To parse such XML, pass an `xmlFeatures` struct as the `validator` argument:
19+
20+
```luceescript+trycf
21+
xmlWithDoctype = '<?xml version="1.0"?>
22+
<!DOCTYPE hibernate-mapping PUBLIC
23+
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
24+
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
25+
<hibernate-mapping></hibernate-mapping>';
26+
27+
// this will throw an error with secure defaults
28+
try {
29+
doc = xmlParse( xmlWithDoctype );
30+
echo( "parsed ok" );
31+
} catch ( e ) {
32+
echo( "Blocked: " & e.message );
33+
}
34+
35+
echo( "<br><br>" );
36+
37+
// override xmlFeatures to allow DOCTYPE for this call only
38+
doc = xmlParse( xmlWithDoctype, false, {
39+
"secure": false,
40+
"disallowDoctypeDecl": false,
41+
"externalGeneralEntities": false
42+
} );
43+
echo( "Parsed with override: " & doc.xmlRoot.xmlName );
44+
```
45+
46+
See the [XML Security with xmlFeatures](/docs/recipes/xml-security-xmlfeatures) recipe for more details.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Since Lucee 5.4.2 and 6.0, XML parsing is secure by default — DOCTYPE declarations and external entities are blocked to prevent XXE attacks.
2+
3+
You can pass a struct of `xmlFeatures` as the `validator` argument to override the security settings for a single parse call, or set `this.xmlFeatures` in `Application.cfc` for application-wide configuration.
4+
5+
See the [XML Security with xmlFeatures](/docs/recipes/xml-security-xmlfeatures) recipe for full details and examples.
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
<!--
2+
{
3+
"title": "XML Security with xmlFeatures",
4+
"id": "xml-security-xmlfeatures",
5+
"menuTitle": "XML Security (xmlFeatures)",
6+
"description": "How to control XML parsing security in Lucee using xmlFeatures to protect against XXE attacks and other XML vulnerabilities.",
7+
"keywords": [
8+
"XML",
9+
"xmlFeatures",
10+
"XXE",
11+
"security",
12+
"xmlParse",
13+
"isXml",
14+
"external entities",
15+
"DOCTYPE"
16+
],
17+
"categories": [
18+
"xml",
19+
"security"
20+
],
21+
"related": [
22+
"function-xmlparse",
23+
"function-isxml",
24+
"function-xmlsearch"
25+
]
26+
}
27+
-->
28+
29+
# XML Security with xmlFeatures
30+
31+
Since Lucee 5.4.2 and 6.0, XML parsing is **secure by default** to protect against [XML External Entity (XXE)](https://owasp.org/www-community/vulnerabilities/XML_External_Entity_(XXE)_Processing) attacks. DOCTYPE declarations and external entities are blocked out of the box.
32+
33+
You can control XML security settings at two levels:
34+
35+
- **Application-wide** via `this.xmlFeatures` in `Application.cfc`
36+
- **Per-call** by passing a struct as the `validator` argument to `xmlParse()`, or as the `xmlFeatures` argument to `isXml()`
37+
38+
Per-call settings override the application-level settings for that single operation.
39+
40+
## Built-in Feature Keys
41+
42+
| Key | Type | Default | Description |
43+
|-----|------|---------|-------------|
44+
| `secure` | boolean | `true` | Master switch. When `true`, applies the full [OWASP XXE prevention](https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html) settings: disallows DOCTYPE, disables external entities and parameter entities, disables external DTD loading, disables XInclude, and blocks access to external DTDs and schemas. |
45+
| `disallowDoctypeDecl` | boolean | `true` | When `true`, any XML containing a `<!DOCTYPE>` declaration throws an error. |
46+
| `externalGeneralEntities` | boolean | `false` | When `true`, allows the parser to resolve external entity references (e.g. `<!ENTITY xxe SYSTEM "file:///etc/passwd">`). |
47+
| `allowExternalEntities` | boolean | `false` | Adobe ColdFusion compatibility alias for `externalGeneralEntities`. If both are set, their values must match or an error is thrown. |
48+
49+
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.
50+
51+
## Setting xmlFeatures in Application.cfc
52+
53+
The defaults are secure, so you only need to set `this.xmlFeatures` if you need to relax the restrictions:
54+
55+
```luceescript
56+
// Application.cfc
57+
component {
58+
this.name = "myApp";
59+
60+
// these are the defaults, you don't need to set them explicitly
61+
this.xmlFeatures = {
62+
"secure": true,
63+
"disallowDoctypeDecl": true,
64+
"externalGeneralEntities": false
65+
};
66+
}
67+
```
68+
69+
To allow DOCTYPE declarations (e.g. for Hibernate mapping files or legacy XML with DTD references):
70+
71+
```luceescript
72+
// Application.cfc
73+
component {
74+
this.name = "myApp";
75+
76+
this.xmlFeatures = {
77+
"secure": false,
78+
"disallowDoctypeDecl": false,
79+
"externalGeneralEntities": false
80+
};
81+
}
82+
```
83+
84+
You can also update the settings at runtime using `application action="update"`:
85+
86+
```luceescript
87+
application action="update" xmlFeatures = {
88+
"secure": false,
89+
"disallowDoctypeDecl": false,
90+
"externalGeneralEntities": false
91+
};
92+
```
93+
94+
## Per-call Override with xmlParse()
95+
96+
You can override the application-level settings for a single `xmlParse()` call by passing a struct as the `validator` argument (the third argument):
97+
98+
```luceescript
99+
// application has secure defaults, but this specific call allows DOCTYPE
100+
xml = xmlParse( xmlString, false, {
101+
"secure": false,
102+
"disallowDoctypeDecl": false,
103+
"externalGeneralEntities": false
104+
} );
105+
```
106+
107+
This is useful when your application is secure by default but you need to parse a specific document that contains a DOCTYPE declaration.
108+
109+
## Per-call Override with isXml()
110+
111+
Similarly, `isXml()` accepts an `xmlFeatures` struct as its second argument:
112+
113+
```luceescript
114+
// check if a string is valid XML, allowing DOCTYPE
115+
result = isXml( xmlString, {
116+
"secure": false,
117+
"disallowDoctypeDecl": false,
118+
"externalGeneralEntities": false
119+
} );
120+
```
121+
122+
## Why the Defaults Block DOCTYPE
123+
124+
By default, XML with a DOCTYPE declaration is rejected:
125+
126+
```luceescript
127+
xmlString = '<?xml version="1.0"?>
128+
<!DOCTYPE foo [
129+
<!ENTITY xxe SYSTEM "file:///etc/passwd">
130+
]>
131+
<foo>&xxe;</foo>';
132+
133+
try {
134+
doc = xmlParse( xmlString );
135+
} catch ( e ) {
136+
// "DOCTYPE is disallowed when the feature
137+
// http://apache.org/xml/features/disallow-doctype-decl set to true"
138+
echo( e.message );
139+
}
140+
```
141+
142+
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.
143+
144+
## Pass-through Features
145+
146+
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](https://xerces.apache.org/xerces2-j/features.html):
147+
148+
```luceescript
149+
this.xmlFeatures = {
150+
"secure": false,
151+
"disallowDoctypeDecl": false,
152+
"externalGeneralEntities": true,
153+
"http://apache.org/xml/features/validation/id-idref-checking": true
154+
};
155+
```
156+
157+
You can also use the full Xerces URI form for the built-in features:
158+
159+
```luceescript
160+
// equivalent to disallowDoctypeDecl: true
161+
application action="update" xmlFeatures = {
162+
"http://apache.org/xml/features/disallow-doctype-decl": true
163+
};
164+
```
165+
166+
## System-level Lock-down
167+
168+
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.
169+
170+
This is useful for shared hosting environments where you want to enforce XML security across all applications.
171+
172+
```
173+
-Dlucee.xmlfeatures.override.disable=true
174+
```
175+
176+
Or as an environment variable:
177+
178+
```
179+
LUCEE_XMLFEATURES_OVERRIDE_DISABLE=true
180+
```
181+
182+
## What `secure: true` Does Under the Hood
183+
184+
When `secure` is `true`, Lucee configures the Java XML parser following the [OWASP XXE Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/XML_External_Entity_Prevention_Cheat_Sheet.html):
185+
186+
- `http://apache.org/xml/features/disallow-doctype-decl` = `true`
187+
- `http://xml.org/sax/features/external-general-entities` = `false`
188+
- `http://xml.org/sax/features/external-parameter-entities` = `false`
189+
- `http://apache.org/xml/features/nonvalidating/load-external-dtd` = `false`
190+
- XInclude aware = `false`
191+
- Expand entity references = `false`
192+
- Access to external DTD = blocked
193+
- Access to external schema = blocked
194+
195+
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.

0 commit comments

Comments
 (0)