Swift Foundation XML External Entities (XXE)

Swift Foundation XML external entities (XXE)

Description

XML external entity (XXE) vulnerabilities arise when DTDs or entity declarations in untrusted XML are processed unsafely. An attacker may cause a parser to read local files or network resources, or exhaust resources through repeated entity expansion.

Foundation's XMLParser.shouldResolveExternalEntities defaults to false, and the newer externalEntityResolvingPolicy defaults to .never. Retain these protective defaults. Apple warns that enabling shouldResolveExternalEntities may trigger network or disk I/O when loading an external DTD. The actual effect depends on the resolution policy and XMLParserDelegate implementation.

Potential impact

  • Exposure of local files or application data
  • Server-side request forgery (SSRF) or port probing against internal services
  • Memory and CPU exhaustion from repeated entity expansion or large DTDs
  • Out-of-band data exfiltration even when results are absent from the response

Remediation

  1. For untrusted XML, leave shouldResolveExternalEntities at its default or set it to false before parse().
  2. On newer Foundation targets, also keep externalEntityResolvingPolicy at .never. Ensure later code does not re-enable either setting.
  3. Do not treat .noNetwork as a general solution: It blocks network loads but does not disable external entity resolution itself.
  4. If external entities are essential, separate that workflow from untrusted XML processing and permit only explicitly preapproved entity URLs.
  5. Limit input size and processing time as additional measures, not substitutes for disabling entity resolution.

Examples

Before

swift
import Foundation

func parseRemoteXML() throws {
    let remoteData = try Data(
        contentsOf: URL(string: "https://example.com/input.xml")!
    )
    let parser = Foundation.XMLParser(data: remoteData)
    parser.externalEntityResolvingPolicy = .always
    parser.shouldResolveExternalEntities = true
    _ = parser.parse()
}

After

swift
import Foundation

func parseRemoteXML() throws {
    let remoteData = try Data(
        contentsOf: URL(string: "https://example.com/input.xml")!
    )
    let parser = Foundation.XMLParser(data: remoteData)
    parser.externalEntityResolvingPolicy = .never
    parser.shouldResolveExternalEntities = false
    _ = parser.parse()
}

Explanation:

  • Before: The parser permits external entity loading before parsing remote XML.
  • After: Both the newer policy and Boolean flag are set to protective values before parsing. Apply false and .never before calling parse().

References