blob: d73fbe35d22e4e660ac601f7024d3e8c8ddf6a0d [file] [view]
# Apache Santuario (XML Security for Java) — Threat Model
## §1 Header
- **Project:** Apache Santuario XML Security for Java (`apache/santuario-xml-security-java`),
artifact `org.apache.santuario:xmlsec`.
- **Written against:** `main` @ HEAD (2026-06), 4.0.x/3.0.x line.
- **Author:** ASF Security team, drafted via the threat-model-producer rubric
(Michael Scovetta rubric) at the Apache Santuario PMC's request (path 3).
- **Status:** DRAFT — under maintainer review (2026-06-10). Not yet ratified.
- **Version binding:** versioned with the project; a report against version *N*
is triaged against the model as it stood at *N*.
- **Reporting cross-reference:** §8-violating findings go to the disclosure
channel in [`SECURITY.md`](SECURITY.md); §3/§9 findings are closed citing
this document.
- **Provenance legend:** *(documented)* = project source / `SECURITY.md` /
advisories (cited); *(maintainer)* = a Santuario maintainer in this review;
*(inferred)* = reasoned from code/domain, not yet confirmed — each has a §14
open question.
- **Draft confidence:** ~26 documented / 0 maintainer / 22 inferred.
**What it is.** A Java library implementing the W3C XML Signature and XML
Encryption standards (plus the JSR-105 javax.xml.crypto API). It is consumed
**in-process** by applications and frameworks (notably WS-Security stacks —
WSS4J, CXF, OpenSAML/Shibboleth, Apache Santuario users in SAML/SOAP) to
**sign, verify, encrypt, and decrypt XML**. It ships two implementations: a
mature **DOM**-based stack and a newer **StAX** (streaming) stack. The
security-critical operations are *verification* and *decryption* of
**attacker-controlled XML**.
## §2 Scope and intended use
Intended use: an application calls the library to verify a signature on, or
decrypt, an XML document that **originates outside the application's trust
boundary** (a SOAP message, a SAML assertion, a signed document). Equally, to
produce signatures/ciphertext over its own (trusted) data.
Caller trust level: the **calling application is trusted**; the **XML input to
verification/decryption is untrusted and adversary-controlled**. This is the
defining trust split of the library. *(inferred Q1.)*
**Component families.**
| Family | Entry point | Processes untrusted XML? | In model? |
| --- | --- | --- | --- |
| DOM XML Signature (verify) | `XMLSignature.checkSignatureValue`, `SignedInfo` | **yes** | **Yes** |
| DOM XML Encryption (decrypt) | `XMLCipher.doFinal` / `decryptData` | **yes** | **Yes** |
| StAX (streaming) Sig/Enc | `InboundXMLSec`, `*InputProcessor` | **yes** | **Yes** |
| Transforms / canonicalization | `Transform`, `Transforms`, `Canonicalizer` | **yes** (transform chain runs on attacker XML) | **Yes** |
| Key resolution | `KeyResolver`, `*KeyResolverSpi` | **yes** (`KeyInfo` is attacker-supplied) | **Yes** |
| Signing / encryption (produce) | `XMLSignature.sign`, `XMLCipher.encryptData` | no over trusted data | partial see §3 |
| JSR-105 API surface | `javax.xml.crypto.dsig.*` | as above | **Yes** (delegates to DOM) |
## §3 Out of scope (explicit non-goals)
- **Establishing trust in the verifying key.** The library tells the caller
*"this signature is cryptographically valid under key K"* and can resolve K
from the message's `KeyInfo`. It does **not** decide whether K is *trusted*.
Validating the certificate chain / matching K against an allow-list is the
**caller's** responsibility 10). A report that boils down to "I trusted a
self-signed cert in `KeyInfo`" is `OUT-OF-MODEL: trusted-input` /
`BY-DESIGN`. *(documented KeyResolver returns keys; trust decisions are not
in this layer.)*
- **What the signature covers.** The library verifies that *referenced*
content has a valid digest; it does **not** decide whether the *right*
content was signed. Checking that the signature covers the
security-relevant elements (the XML-Signature-Wrapping defence) is the
caller's job — see §9/§11. *(documented — this is the canonical XSW
caveat.)*
- **Retired C++ project** (`santuario-xml-security-cpp`) and the two archived
repos — out of scope. *(documented — SECURITY.md "retired".)*
- **The application's XML toolchain after extraction.** Once the caller pulls
decrypted/verified content out and re-parses or deserializes it, that is the
caller's pipeline.
## §4 Trust boundaries and data flow
The boundary is **the XML document submitted for verification/decryption**.
Everything in that document — elements, `Reference` URIs, `Transform` chains,
`KeyInfo`, `EncryptedKey`, algorithm identifiers, namespace prefixes — is
**attacker-controlled** and must be treated as hostile.
```
untrusted XML (SOAP/SAML/doc)
│ attacker controls structure, transforms, KeyInfo, algorithms
parse (DocumentBuilderFactory — DTD handling = §8/§5a)
Signature verify: resolve References -> run Transform chain -> canonicalize -> digest -> verify sig
│ ▲ XSLT/XPath transforms run here (§9 false friend)
Decrypt: resolve EncryptedKey -> KeyResolver -> unwrap -> decrypt -> (optional) re-parse
result handed to caller ──► caller must check WHO signed + WHAT was signed (§10)
```
**Reachability precondition (the triager's test):** a finding is in-model only
if reachable from the untrusted XML during verify/decrypt **with secure
validation enabled** 5a). A finding that requires secure validation to be
*off*, or requires the caller to skip trust/coverage checks, is out of model
5a / §3).
## §5 Assumptions about the environment
- **Runtime:** a conformant JRE with a JAXP XML parser and a JCE provider for
the crypto primitives. The library relies on the platform parser's DTD/entity
controls (§5a). *(inferred — Q2.)*
- **JCE provider:** primitive correctness (RSA/AES/SHA, GCM nonce handling) is
the provider's; the library composes them. *(inferred Q3.)*
- **Concurrency:** `XMLSignature`/`XMLCipher` instances are not assumed
thread-safe; per-operation instances are the expected usage. *(inferred
Q4.)*
- **No ambient side effects** beyond what a `Transform`/`KeyResolver` is
configured to do but note the default-resolver and transform behaviour can
dereference URIs 9). *(inferred Q5, high priority.)*
## §5a Build-time and configuration variants — **the central knob**
**Secure validation mode is the load-bearing configuration.** Santuario
exposes a "secure validation" flag (e.g. `XMLSignature` /
`XMLCipher.setSecureValidation(true)`, the
`org.apache.xml.security.secureValidation` property, and on by default in some
JSR-105 paths). With secure validation **on**, the library:
- rejects DTDs / disables external entity and `RetrievalMethod`/`KeyInfo`
remote dereferencing,
- forbids dangerous transforms (notably **XSLT**),
- enforces minimum key sizes and rejects known-weak digest/MAC/signature
algorithms (e.g. MD5, SHA-1-in-signatures, RSA < 1024-ish, HMAC truncation),
- caps the number of `Reference`/`Transform` elements to bound expansion.
*(documented these are the historical CVE mitigations folded into secure
validation; exact set is Q-confirmed in §14.)*
**The insecure-default problem (must be resolved by the maintainer).** Whether
secure validation is **on by default** depends on entry point and version
(the JSR-105 `javax.xml.crypto` path vs the native `org.apache.xml.security`
path have differed). This reshapes §8911a13 simultaneously:
- If secure validation is the **supported posture for untrusted input** and a
caller must opt in, then a report that only manifests with it **off** is
`OUT-OF-MODEL: non-default-build` and §10 carries "enable secure
validation."
- If it is **on by default** everywhere, the §8 properties hold out of the
box. **This is wave-1 Q6 and the most important question in the model.**
## §6 Assumptions about inputs
Per-parameter trust for the security-critical entry points:
| Entry point | Parameter | Attacker-controllable? | Caller must enforce |
| --- | --- | --- | --- |
| signature verify | the XML document / `SignedInfo` | **yes** | enable secure validation; check coverage 10) |
| signature verify | `KeyInfo` (embedded key/cert) | **yes** | establish trust in the resolved key do not trust `KeyInfo` blindly |
| signature verify | `Reference` URI / `Transform` chain | **yes** | rely on secure validation to bound transforms/derefs |
| decrypt | `EncryptedData` / `EncryptedKey` | **yes** | secure validation; treat decrypted bytes as untrusted |
| decrypt | algorithm identifiers | **yes** | secure validation enforces allow-list |
| sign / encrypt | content to protect, signing key | **no** caller-supplied trusted | protect the private key (caller) |
Plus: the parser-level inputs (DOCTYPE, entities, external refs) are
attacker-controlled and governed by §5a8.
## §7 Adversary model
- **In scope:** the party that supplies the XML being verified or decrypted
a network peer in a WS-Security/SAML exchange, the sender of a signed
document. Capabilities: full control of the XML structure, transforms,
`KeyInfo`, algorithms; ability to craft signature-wrapping, transform, key-
confusion, decryption-oracle, and resource-exhaustion payloads. *(inferred
Q1.)*
- **Out of scope:** an attacker who controls the calling process or the
trust/keystore configuration (they have already won); side-channel/co-tenant
adversaries against the JCE provider 5); an attacker who can make the
caller *disable* secure validation or skip trust checks.
## §8 Security properties the project provides (with secure validation on)
1. **DTD / XXE rejection.** Secure validation rejects DOCTYPE and external
entity resolution during parse/verify. *Violation:* external entity
resolved / SSRF / file read. *Severity:* critical. *(documented secure
validation; Q6 for default.)*
2. **Dangerous-transform rejection.** XSLT (and other code-executing/remote-
deref transforms) are disallowed under secure validation. *Violation:*
XSLT/script or remote fetch via a `Transform`. *Severity:* critical.
*(documented.)*
3. **Weak-algorithm rejection.** Known-weak digest/MAC/signature algorithms and
undersized keys are rejected under secure validation. *Violation:* a
signature accepted under MD5/short-HMAC/short-RSA. *Severity:* critical.
*(documented folds prior CVEs.)*
4. **Expansion bounding.** The number of `Reference`/`Transform` elements is
capped to bound CPU/memory under secure validation. *Violation:* super-
linear blowup from many references/transforms. *Severity:* security
(DoS). *(inferred Q7: confirm the exact caps.)*
5. **Cryptographic correctness of the verify/decrypt operation** given a
well-formed signature and a provided key i.e. an invalid signature does
not verify true. *Violation:* signature forgery / bypass. *Severity:*
critical. *(documented core function.)*
## §9 Security properties the project does *not* provide
- **It does not tell you the signature covers the right thing.** A valid
signature over *some* element is not a signature over the element your
application cares about. **XML Signature Wrapping (XSW)** lives entirely in
this gap and is the #1 real-world Santuario-adjacent vulnerability class —
it is the *caller's* responsibility to verify that the verified `Reference`
resolves to the security-relevant content. *(documented — canonical caveat.)*
- *False friend:* "the signature verified" is **not** "the document is
authentic and unmodified where it matters."
- **It does not establish key trust.** Resolving a key from `KeyInfo` is a
convenience, not a trust decision; certificate-path validation / key pinning
is the caller's 310).
- **No protection if secure validation is disabled.** With it off, XXE, XSLT
transforms, weak algorithms, and unbounded expansion are all reachable by
design, for backward-compatible processing of *trusted* XML. *(documented
§5a.)*
- **No decryption-oracle hardening at the protocol layer.** The library
performs the cryptographic decrypt; padding-oracle / replay / "decrypt-then-
verify ordering" defences are protocol-level (WS-Security/SAML) concerns the
caller's stack must handle. *(inferred — Q8.)*
- **Well-known attack classes the integrator owns:** XSW (coverage checking),
XXE/billion-laughs when secure validation is off, key-confusion via
unvalidated `KeyInfo`, and decryption oracles at the protocol layer.
## §10 Downstream responsibilities
- **Enable secure validation** for any XML from outside your trust boundary.
- **Check what was signed** — verify the signature's `Reference`s resolve to
the exact content you are about to trust (defeat XSW); prefer ID-based
references you control and re-check post-verify.
- **Establish key trust independently** validate the cert chain / match the
key against an allow-list; never trust `KeyInfo`-embedded keys implicitly.
- **Treat decrypted/verified bytes as untrusted** until your own validation
passes; apply resource limits at the boundary.
- Protect signing/decryption private keys (caller-side key management).
## §11 Known misuse patterns
- **Trusting a valid signature without checking coverage** XSW.
- **Trusting `KeyInfo` keys/certs** without path validation accept attacker's
key.
- **Processing untrusted XML with secure validation off** → XXE / XSLT / weak-
algo / expansion.
- **Using verification as authorization** without binding the signer identity
to an authorization decision.
## §11a Known non-findings (recurring false positives)
- **`DocumentBuilderFactory` / transform / weak-algo reachable with secure
validation OFF** — non-finding: off is the trusted-input posture (§5a);
reachability precondition (secure validation on) not met.
`OUT-OF-MODEL: non-default-build` (pending Q6).
- **"KeyInfo lets an attacker supply a key"** — non-finding: key *trust* is the
caller's 39). `BY-DESIGN`.
- **"A valid signature didn't protect element X"** where X wasn't referenced —
non-finding: coverage checking is the caller's 9). `BY-DESIGN` /
`VALID-HARDENING` only if the API makes the safe check unreasonably hard.
- **Findings in the retired C++ project / archived repos** `OUT-OF-MODEL:
unsupported-component` 3).
- **JCE-provider primitive issues** (RNG, GCM) out of layer 5).
## §12 Conditions that would change this model
- A change to secure-validation defaults or the set of checks it performs 5a).
- A new transform/canonicalization algorithm, or a new key-resolver that
dereferences remote material.
- The StAX stack diverging from the DOM stack on any §8 property.
- A report that cannot be routed to a §13 disposition revise §89.
## §13 Triage dispositions
| Disposition | Meaning | Licensed by |
| --- | --- | --- |
| `VALID` | A §8 property breaks with secure validation on, via untrusted XML. | §8, §6, §7 |
| `VALID-HARDENING` | No §8 break, but the API makes a §11 misuse (esp. XSW coverage) too easy. | §11 |
| `OUT-OF-MODEL: trusted-input` | Requires attacker control of key-trust / config the model trusts. | §6 |
| `OUT-OF-MODEL: adversary-not-in-scope` | Requires an excluded capability. | §7 |
| `OUT-OF-MODEL: non-default-build` | Only manifests with secure validation off. | §5a |
| `OUT-OF-MODEL: unsupported-component` | Retired C++ / archived repos. | §3 |
| `BY-DESIGN: property-disclaimed` | Key trust, coverage checking, protocol oracles. | §9 |
| `KNOWN-NON-FINDING` | Matches §11a. | §11a |
| `MODEL-GAP` | Unroutable. | triggers §12 |
## §14 Open questions for the maintainers
**Wave 1 the load-bearing ones.**
- **Q1.** Confirm the trust split: calling application trusted; the XML
submitted to verify/decrypt is fully attacker-controlled; the in-scope
adversary is the XML supplier. 247.)
- **Q6.** **Secure validation defaults** for each entry point (native
`org.apache.xml.security` vs JSR-105 `javax.xml.crypto`, DOM vs StAX, and by
version), is secure validation **on by default**, or must the caller opt in?
This decides whether secure-validation-off findings are `VALID` or
`OUT-OF-MODEL: non-default-build`. 5a8911a13.)
- **Q-XSW.** Confirm the project's official position that **signature-coverage
checking (XSW defence) is the caller's responsibility**, and point at the
canonical guidance you want cited when closing XSW reports. 910.)
**Wave 2 the secure-validation checklist (confirm/correct the §8 list).**
- **Q7.** Exact set enforced by secure validation: which algorithms/key sizes
are rejected, which transforms are forbidden, and the caps on
`Reference`/`Transform` counts? 8.)
- **Q3/Q5.** Which transforms or key-resolvers can dereference remote/local
URIs, and are any enabled even under secure validation? 589.)
- **Q8.** Do you make any protocol-level guarantee (decryption-oracle, replay,
decrypt/verify ordering), or is that explicitly the WS-Security/SAML caller's
layer? (§9.)
**Wave 3 — surface & coexistence.**
- **Q2/Q4.** Parser/JCE assumptions and thread-safety expectations. (§5.)
- **Q9.** This adds `THREAT_MODEL.md` + `AGENTS.md` alongside your existing
`SECURITY.md` (which we kept). Confirm the disclosure pointer and whether the
model should become canonical with `SECURITY.md` linking to it. (§1/§15.)
## §15 Appendix — existing-policy back-map
The repo's `SECURITY.md` is a disclosure-process + supported-versions policy
(no embedded threat model); it points at the ASF security process and the
published advisories at `santuario.apache.org/secadv.html`. This
`THREAT_MODEL.md` is additive `SECURITY.md` is preserved and gains a pointer
to the model. The published advisories are a high-value source for refining
§811a in a later pass (each historical CVE maps to a secure-validation
mitigation).