The second family abuses internal entities: each entity refers to several copies of the previous one, so a document of a few hundred bytes expands to gigabytes ("billion laughs"), or one large entity is referenced many times (quadratic blowup). It is a denial-of-service attack on memory and CPU, and it needs no network access.
Modern parsers cap the expansion. In lxml vs ElementTree, Expat 1,381 (through Python 3.14) stopped with "limit on input amplification factor (from DTD and entities) breached" and libxml2 3,427 with "Maximum entity amplification factor exceeded". The JDK enforces entity-expansion limits when FEATURE_SECURE_PROCESSING is on, and defusedxml refuses the entity declarations before any expansion starts.
| Parser | Built-in protection observed or documented | Extra step |
|---|---|---|
| Expat 2.7 (Python stdlib) | Amplification limit | defusedxml for policy |
| libxml2 2.14-2.15 (lxml 3,063 , PHP) | Amplification limit | Keep huge_tree off |
| JDK (Xerces 129 ) | Limits with secure processing | Disallow DOCTYPE |
| .NET XmlReader | DTDs prohibited by default | Keep Prohibit |
Limits are a safety net: also cap file sizes, parse with a timeout, and refuse DTDs the contract lacks.