This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ws-wss4j.git
The following commit(s) were added to refs/heads/master by this push:
new 636e932b9 Add draft project security threat-model document (#618)
636e932b9 is described below
commit 636e932b9a37a912b06a464dcdd828ed4358ab60
Author: Jarek Potiuk <[email protected]>
AuthorDate: Mon Jul 20 12:10:55 2026 +0200
Add draft project security threat-model document (#618)
Adds a draft project-level security threat-model document
(draft-THREAT-MODEL.md) at repo root, improving discoverability
for automated security scanners running against this repository.
The file follows the rubric format used by several other ASF
projects piloting security-model discoverability.
The "draft-" prefix signals this is a proposal for the PMC to
review, correct, or reject — not a finalised maintainer-blessed
model. Every claim carries a provenance tag (documented /
inferred / maintainer) so reviewers can see where each claim
originates; §14 collects open questions for the maintainers.
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
---
draft-THREAT-MODEL.md | 967 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 967 insertions(+)
diff --git a/draft-THREAT-MODEL.md b/draft-THREAT-MODEL.md
new file mode 100644
index 000000000..8ee4a6e16
--- /dev/null
+++ b/draft-THREAT-MODEL.md
@@ -0,0 +1,967 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+# Apache WSS4J Security Threat Model (draft)
+
+## §1 Header
+
+- **Project**: Apache WSS4J — a Java implementation of the OASIS WS-Security
+ specifications (SOAP Message Security 1.1, UsernameToken Profile 1.1,
+ X.509 Certificate Token Profile 1.1, SAML Token Profile 1.1, Kerberos
+ Token Profile 1.1, SwA Profile 1.1, Basic Security Profile 1.1)
+ *(documented: `README.md`, `src/site/asciidoc/what.adoc`)*.
+- **Repository**: `apache/ws-wss4j`.
+- **Version / commit**: this model is drafted against the default branch
+ (`master`) at clone time. A report against project release *N* should be
+ triaged against the model as it stood at *N*, not at HEAD.
+- **Supported branches** (per `SECURITY.md`): 4.0.x, 3.0.x, 2.4.x. Earlier
+ branches are explicitly out of support.
+- **Date**: 2026-05-30.
+- **Authors**: ASF Security team draft, awaiting WSS4J / Webservices PMC
+ review.
+- **Status**: draft — under maintainer review.
+- **Reporting**: vulnerabilities that fall under §8 (claimed properties)
+ should be reported per the Apache Security Team disclosure channel
+ (linked from `SECURITY.md` → <https://www.apache.org/security/>); reports
+ that fall under §3 (out of scope) or §9 (properties not provided) will
+ be closed by WSS4J triagers citing this document. Existing project
+ advisories are published at
+ <http://ws.apache.org/wss4j/security_advisories.html> *(documented:
+ `src/site/xdoc/security_advisories.xml`)*.
+- **Provenance legend** —
+ *(documented)* = drawn from in-repo docs / project website / source-tree
+ source comments, with citation;
+ *(maintainer)* = stated by a WSS4J maintainer in response to this draft;
+ *(inferred)* = synthesized by the producer from code structure or
+ domain knowledge, awaiting PMC ratification (every *(inferred)* tag has
+ a matching §14 question).
+- **Draft confidence**: 39 documented / 0 maintainer / 28 inferred.
+
+WSS4J is a library — not a service, not a daemon, not a SOAP stack of
+its own. It is designed to be embedded by a Web Services stack such as
+Apache CXF or Apache Axis to apply and validate the WS-Security message
+header on SOAP envelopes: signature, encryption, timestamps, replay
+caches, and security tokens (UsernameToken, X.509, SAML, Kerberos,
+SecurityContextToken, IssuedToken, DerivedKey). Cryptographic primitives
+are delegated to Apache Santuario (XML Signature / XML Encryption), the
+JDK's JCE provider (TLS, MessageDigest, KeyStore), Bouncy Castle (optional
+BC provider, OpenSAML's BC use), and OpenSAML (SAML assertion
+construction and processing). WSS4J ships two distinct processing
+engines: a DOM-based engine (`ws-security-dom`) and a streaming StAX-based
+engine (`ws-security-stax`) introduced in 2.0.0.
+
+## §2 Scope and intended use
+
+### Intended use
+
+- In-process production of and verification of WS-Security message headers
+ on SOAP 1.1 / 1.2 envelopes, invoked by a Web Services stack
+ (Apache CXF, Apache Axis) on the host application's behalf
+ *(documented: `README.md`, `src/site/asciidoc/what.adoc`,
+ `src/site/asciidoc/using.adoc`)*.
+- Two API styles are documented as supported: the "actions" approach
+ (`WSHandlerConstants.ACTION`-driven handlers) and the
+ WS-SecurityPolicy-driven approach (PolicyEnforcer in
+ `ws-security-policy-stax`). The best-practice guide
+ *strongly favors* WS-SecurityPolicy because it provides automatic
+ protection against XML signature wrapping
+ *(documented: `src/site/asciidoc/best_practice.adoc` §"Use
+ WS-SecurityPolicy to enforce security requirements")*.
+
+### Deployment shape
+
+WSS4J is **not** a network service, **not** a daemon, **not** a SOAP
+stack. It is an in-process Java library whose callers are SOAP stacks
+that own the network surface. The threat model is therefore that of a
+security-rich library *(documented: `README.md`,
+`src/site/asciidoc/what.adoc`)*.
+
+### Caller roles (library model)
+
+| Role | Trust level | Notes |
+| --- | --- | --- |
+| **Embedding SOAP stack** (CXF / Axis / Spring-WS) | trusted | Calls WSS4J
entry points, supplies `RequestData` / `WSSSecurityProperties`, supplies the
SOAP envelope as a `Document` (DOM) or `XMLStreamReader` (StAX), holds
keystores, supplies `CallbackHandler`. WSS4J does no peer authentication of the
embedding stack. |
+| **CallbackHandler implementation** | trusted | Provides passwords,
decryption keys, attachments. WSS4J does not validate that the CallbackHandler
is well-behaved; a hostile CallbackHandler can read arbitrary plaintext or leak
any key WSS4J asks for *(inferred — §14 Q1)*. |
+| **Validator implementation** | trusted | Plugs into the inbound chain via
`WSSConfig` to validate UsernameToken, SAML assertion, Timestamp, signature,
etc. WSS4J ships a default set; replacing them is a documented extension point
*(documented: `src/site/asciidoc/topics.adoc` §"Introducing Validators")*. |
+| **Wire-side peer of the SOAP message** | **untrusted** | The producer of the
bytes WSS4J actually parses. This is the only untrusted role. The peer is the
adversary in §7. |
+| **Keystore / TrustStore on disk** | trusted | Supplies signing keys and the
set of CAs WSS4J will accept *(documented: `src/site/asciidoc/topics.adoc`
§"Crypto Interface", `src/site/asciidoc/config.adoc` §"Crypto properties")*. |
+| **Apache Santuario, OpenSAML, Bouncy Castle, JCE provider** | trusted
upstream | WSS4J vendors none of these; vulnerabilities intrinsic to these
upstreams are reported there *(inferred — §14 Q2)*. |
+
+### Component-family table
+
+| Family | Representative entry point | Touches outside the process? |
In-model? |
+| --- | --- | --- | --- |
+| `ws-security-common` — Crypto interface (`Merlin`, `CertificateStore`,
`MerlinDevice`), `ReplayCache` (`EHCacheReplayCache`, `MemoryReplayCache`),
`ConfigurationConstants`, BSP rule enforcement, derived-key util, SAML
assertion wrapper, SPNEGO util, JAAS callback wrapper | static config /
file-backed keystore / file-backed EHCache | **yes** (file-backed surfaces
noted as caller-trusted) |
+| `ws-security-dom` — DOM-based inbound `WSSecurityEngine`, processors per
element (Signature, Encryption, Timestamp, UsernameToken, SAML, Kerberos, SCT,
…), `WSHandler` action-based driver | invoked from CXF / Axis interceptor with
a parsed `org.w3c.dom.Document` *(documented: `src/site/asciidoc/topics.adoc`
§"Specifying elements to sign or encrypt")* | **yes** — primary
attacker-controlled surface |
+| `ws-security-stax` — streaming StAX-based inbound/outbound (`InboundWSSec`,
`OutboundWSSec`, `WSSec`); installs a security-specific `XMLStreamReader`
wrapper | invoked from CXF Stax interceptor with caller-supplied
`XMLStreamReader` *(documented: `src/site/asciidoc/streaming.adoc`)* | **yes**
— primary attacker-controlled surface |
+| `ws-security-policy-stax` — WS-SecurityPolicy enforcement layered on the
StAX engine (`PolicyEnforcer`, `PolicyInputProcessor`) | n/a (operates on the
StAX event stream) | **yes** |
+| `ws-security-web` — servlet `Filter` and utility for receiving WS-Security
messages via plain HTTP, used in some legacy deployments | network listener via
the embedding container | in-model **insofar as WSS4J ships it**; the
surrounding servlet container is out *(inferred — §14 Q3)* |
+| `bindings/` — JAXB-generated XML bindings for WS-Trust,
WS-SecureConversation, WS-Policy schema, `wsu`, `wsse10`/`wsse11`, `xenc`,
`xmldsig` | n/a (DTOs only) | **yes** — used by both engines |
+| `integration/` (test-only, Kerberos integration tests against Apache
Directory) | n/a in production | **out of model** *(§3)* |
+| Sample/test resources (`*/src/test/`, `policy/src/test/...`) — test JKS
keystores with publicly-documented passwords | n/a | **out of model** *(§3)* |
+
+A finding is in-model only if it reaches a row marked **yes**.
+
+## §3 Out of scope (explicit non-goals)
+
+1. **The SOAP stack itself.** WSS4J is invoked by Apache CXF / Apache Axis;
+ the parsing of the HTTP request, the routing of the SOAP body to a
+ service implementation, SOAPAction enforcement, and authentication
+ of the transport layer are all the stack's job. Findings that
+ require the SOAP stack to misbehave (e.g. WSS-456's "SOAPAction
+ spoofing should be left to the SOAP stack" — *(documented:
+
`ws-security-policy-stax/src/test/java/org/apache/wss4j/policy/stax/test/VulnerabliltyVectorsTest.java`*)
+ are out of model. → `OUT-OF-MODEL: adversary-not-in-scope`.
+2. **A SOAP parser.** WSS4J does not parse SOAP from bytes — it receives a
+ `Document` (DOM engine) or `XMLStreamReader` (StAX engine) from the
+ caller. XXE / DTD / billion-laughs defenses on the *XML-bytes-to-DOM*
+ parsing step belong to the caller's parser configuration. WSS4J's
+ `InboundWSSec` javadoc warns this explicitly: *"configure your
+ xmlStreamReader correctly. Otherwise you can create a security
+ hole."* *(documented:
+
`ws-security-stax/src/main/java/org/apache/wss4j/stax/setup/InboundWSSec.java`
line 94-99)*. →
+ `OUT-OF-MODEL: trusted-input`.
+3. **Cryptographic primitives.** WSS4J does not implement RSA, AES, SHA,
+ PKCS#1v1.5, OAEP, GCM, ECDSA, or any other primitive. It composes the
+ JDK's JCE provider, Bouncy Castle (when registered), and Apache
+ Santuario. Defects in the primitives themselves are out of model and
+ reported to the JDK / BC / Santuario projects *(inferred — §14 Q2)*. →
+ `OUT-OF-MODEL: unsupported-component`.
+4. **XML Signature / XML Encryption canonical processing.** Delegated to
+ Apache Santuario; WSS4J does the WS-Security framing
+ *(documented: `README.md` Crypto Notice; `src/site/asciidoc/topics.adoc`
+ §"JSR-105 support")*. → `OUT-OF-MODEL: unsupported-component` for
+ Santuario-internal defects. Where WSS4J *configures* Santuario (e.g.
+ `setSecureValidation`, `setCanonicalizationMethod`,
+ `setEncryptionSerializer`), that configuration is in-model.
+5. **The OpenSAML library.** Used for SAML 1.1 / SAML 2.0 assertion
+ construction and parsing in `ws-security-common/.../saml/`. Defects
+ intrinsic to OpenSAML (XML processing, schema handling) are out of
+ model *(inferred — §14 Q2)*. → `OUT-OF-MODEL: unsupported-component`.
+6. **The Kerberos KDC and SASL/JAAS infrastructure.** WSS4J's Kerberos
+ token support assumes a working JAAS-configured Kerberos environment
+ *(documented: `src/site/asciidoc/wss4j22.adoc` §"Kerberos changes")*. →
+ `OUT-OF-MODEL: trusted-input` for KDC misbehavior.
+7. **EHCache as a Java component.** Default `ReplayCache` implementation
+ is `EHCacheReplayCache`; defects in the EHCache jar itself are
+ out of model *(inferred — §14 Q4)*. → `OUT-OF-MODEL: unsupported-component`.
+8. **Pre-2.4.x branches.** Per `SECURITY.md`, only 4.0.x, 3.0.x, 2.4.x
+ are supported. Reports against 1.x / 2.0.x / 2.1.x / 2.2.x / 2.3.x
+ that are not also reproducible on a supported branch are out of model
+ *(documented: `SECURITY.md`)*.
+9. **The embedding application's password / private-key storage.**
+ WSS4J reads passwords through `WSPasswordCallback` from a caller-supplied
+ `CallbackHandler`. How those passwords are stored, rotated, or audited
+ is the caller's concern *(documented:
+ `src/site/asciidoc/topics.adoc` §"WSPasswordCallback identifiers")*. →
+ `OUT-OF-MODEL: trusted-input`.
+10. **Code shipped in `*/src/test/` and `policy/src/test/`.** Test JKS
+ keystores with documented passwords (e.g. `"security"`, `"changeit"`,
+ `"default"`, `"transmitter.jks"`, `"receiver.jks"`) are sample
+ data, not production keys. Reports that the test keystores have
+ weak passwords or self-signed certs are `OUT-OF-MODEL:
unsupported-component`.
+
+## §4 Trust boundaries and data flow
+
+WSS4J has a small number of well-defined trust transitions; a finding
+is in-model only if it maps to one of them.
+
+| # | Transition | Authentication | Authorization |
+| --- | --- | --- | --- |
+| B1 | Wire peer → SOAP stack → WSS4J `WSSecurityEngine` / `InboundWSSec` (the
bytes of the incoming SOAP envelope's `wsse:Security` header and any
signed/encrypted parts of the body) | the security tokens *inside* the envelope
(UsernameToken, X.509 sig, SAML assertion, Kerberos token) are the
authentication; that is what WSS4J validates | application-layer; not WSS4J's
concern |
+| B2 | Embedding stack → `CallbackHandler` → WSS4J | the embedding stack is
trusted by construction; WSS4J reads passwords and keys via callback | none |
+| B3 | WSS4J → JCE / Santuario / OpenSAML / Bouncy Castle | trusted upstream |
none |
+| B4 | WSS4J → file-backed keystore / truststore / `ReplayCache` directory |
filesystem permissions, owned by the embedding application | OS-level |
+| B5 | WSS4J → SAML token issuer's signing key (for inbound) — trust chain
rooted in the configured TrustStore | X.509 trust chain validation + optional
`SIG_SUBJECT_CERT_CONSTRAINTS` regex | configured per `Merlin` truststore |
+| B6 | WSS4J → KDC (Kerberos token validation) | Kerberos / SPNEGO; relies on
JAAS config | out of WSS4J's hands |
+
+### Reachability preconditions per component
+
+- **DOM engine (`WSSecurityEngine`)**: a finding is in-model only if it
+ is reachable from the bytes of the `wsse:Security` header (or any
+ body part referenced by signature / encryption) supplied by a wire
+ peer. Bytes that arrive through a `CallbackHandler` (passwords, keys)
+ are out-of-model — that's a trusted channel.
+- **Streaming engine (`InboundWSSec`)**: same reachability as above,
+ *plus* the caller is expected to have set
+ `XMLInputFactory.SUPPORT_DTD=false` and
+ `XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES=false` on the
+ `XMLStreamReader` they hand in. WSS4J's *internal*
+ `XML_INPUT_FACTORY` (used for the security-header reparse) sets both
+ to `false` *(documented:
+
`ws-security-stax/src/main/java/org/apache/wss4j/stax/setup/InboundWSSec.java`
lines 63-67)*.
+- **WS-SecurityPolicy enforcement (`ws-security-policy-stax`)**: in-model
+ only when the embedding stack has wired in a `PolicyEnforcer` for the
+ inbound message. Without WS-SecurityPolicy, signature-wrapping
+ defenses described in §11a depend on the caller setting
+ `SIGNATURE_PARTS` / `ENCRYPTION_PARTS` /
`REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS`
+ *(documented: `src/site/asciidoc/best_practice.adoc`,
+ `src/site/asciidoc/config.adoc`)*.
+- **`ws-security-web`**: in-model insofar as the servlet entry point
+ reaches WSS4J; the surrounding servlet container is out.
+- **JAXB bindings**: pure DTO objects, in-model only insofar as the
+ engines call them.
+- **Code in `*/src/test/`**: out of model (§3 item 10).
+
+## §5 Assumptions about the environment
+
+- **JDK**: minimum JDK8 for WSS4J 2.2.x, JDK11 for 3.0.x, JDK17 for 4.0.x
+ *(documented: `src/site/asciidoc/wss4j22.adoc` §"JDK8 minimum
+ requirement"; inferred from `README.md` build link "JDK17"
+ *(inferred — §14 Q5)*)*.
+- **JCE Unlimited Strength**: required for the unit tests; *"if you get
+ errors about invalid key lengths, the Unlimited Strength files are
+ not installed"* *(documented: `README.md` Test Requirements)*. As of
+ Java 8u151 / 9+ this is the default, but on older JDKs WSS4J would
+ use weaker algorithms silently *(inferred — §14 Q6)*.
+- **Apache Santuario** on the classpath, version matching the WSS4J
+ branch *(documented: `README.md` Crypto Notice)*.
+- **OpenSAML 4.x** for WSS4J 3.x+, OpenSAML 3.x for 2.x
+ *(documented: `ChangeLog.txt` WSS-687, WSS-694)*.
+- **Bouncy Castle (optional)** as a JCE provider; WSS4J 2.3.x changed
+ *how* BC is registered *(documented: `ChangeLog.txt` WSS-661)*.
+- **Filesystem**: keystores and truststores are file-backed (via
+ `Merlin`) or `null`-stream-backed (via `MerlinDevice` for HSMs)
+ *(documented: `src/site/asciidoc/config.adoc` §"Merlin Keystore
+ Properties")*. WSS4J does **not** create or rotate these files; the
+ caller does.
+- **Clock**: WSS4J uses the host wall-clock to evaluate
+ `wsu:Timestamp` `Created` / `Expires` semantics. Bounded clock skew
+ is honored via `TTL_TIMESTAMP` (default 300 s) and
+ `TTL_FUTURE_TIMESTAMP` (default 60 s) *(documented:
+ `src/site/asciidoc/config.adoc`)*. If the host clock is wrong, valid
+ signatures may be rejected and replayed messages may be accepted
+ inside the wider window *(inferred — §14 Q7)*.
+- **Memory**: DOM engine holds the entire SOAP envelope in memory;
+ StAX engine streams *(documented: `src/site/asciidoc/streaming.adoc`)*.
+
+### What WSS4J does *not* do to its host (negative claims, awaiting maintainer
ratification)
+
+- Opens **no** listening sockets *(inferred — §14 Q8)*.
+- Spawns **no** child processes *(inferred — §14 Q8)*.
+- Installs **no** signal handlers *(inferred — §14 Q8)*.
+- Reads a small documented set of system properties for extension
+ registry / provider selection (e.g. `org.apache.wss4j.crypto.provider`,
+ the SAMLIssuer system property in 1.6.x); does **not** consume
+ `LD_*`-style envvars for security-sensitive choices *(inferred —
+ §14 Q8)*.
+- Writes log lines via SLF4J at the level the embedder configures;
+ ReplayCache writes EHCache spool files at the documented location
+ *(inferred — §14 Q9)*.
+- Touches the file system only via the JDK `KeyStore`/`Properties`/
+ EHCache APIs at paths the caller supplied
+ *(inferred — §14 Q8)*.
+
+## §5a Build-time and configuration variants
+
+WSS4J ships as a single Maven artifact set (one per module); there are
+no compile-time defines. The runtime configuration knobs in
+`ConfigurationConstants` materially change the security envelope. The
+maintainer-confirmed list is at
+`ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java`
+and `ws-security-dom/.../handler/RequestData.java`; the security-relevant
+subset:
+
+| Flag | Default | Maintainer stance | Effect |
+| --- | --- | --- | --- |
+| `ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM` *("allowRSA15KeyTransportAlgorithm")*
| `false` since 2.0.0 *(documented: `src/site/asciidoc/best_practice.adoc`)* |
hardened default; flipping voids Bleichenbacher defense | enables RSA v1.5
key-transport algorithm |
+| `REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS` | `false` *(documented:
`ws-security-dom/.../RequestData.java` line 74)* | **maintainer ruling
required** — CVE-2015-0227 was about this flag's enforcement *(inferred — §14
Q10)* | requires `EncryptedData` to live in a signed subtree |
+| `IS_BSP_COMPLIANT` *("isBSPCompliant")* | `true` since 1.6 *(documented:
`src/site/asciidoc/topics.adoc` §"Basic Security Profile 1.1 compliance")* |
hardened; disabling weakens inbound checks | enforce BSP 1.1 inbound shape |
+| `TIMESTAMP_STRICT` | `true` *(documented: `src/site/asciidoc/config.adoc`)*
| hardened | rejects past-expiry timestamps; if `false`, expired timestamps are
accepted |
+| `REQUIRE_TIMESTAMP_EXPIRES` | `false` *(documented:
`src/site/asciidoc/config.adoc`)* | optional hardening; flipping forces
explicit expiry | requires `wsu:Timestamp` to carry `Expires` |
+| `TTL_TIMESTAMP` | `300` (s) *(documented)* | tunable; smaller = stricter |
message-validity window |
+| `TTL_FUTURE_TIMESTAMP` | `60` (s) *(documented)* | tunable; smaller =
stricter clock-skew tolerance | how far in the future a `Created` may be |
+| `TTL_USERNAMETOKEN` / `TTL_FUTURE_USERNAMETOKEN` | `300` / `60`
*(documented)* | tunable | analog for UsernameToken `wsu:Created` |
+| `HANDLE_CUSTOM_PASSWORD_TYPES` | `false` *(documented)* | hardened; flipping
accepts non-standard password types | inbound UsernameToken policy |
+| `ALLOW_USERNAMETOKEN_NOPASSWORD` | `false` *(documented)* | hardened;
flipping accepts UT-no-password as deriving primitive | inbound UsernameToken
policy |
+| `PASSWORD_TYPE` (outbound) | `PW_DIGEST` *(documented:
`ws-security-dom/.../RequestData.java`)* | discouraged for
plaintext-over-cleartext; **digest is still relatively weak** | outbound
UsernameToken password encoding |
+| `ENC_KEY_TRANSPORT` *("encryptionKeyTransportAlgorithm")* | RSA-OAEP since
2.0.0 *(documented: `src/site/asciidoc/best_practice.adoc`)* | hardened
default; flipping to RSA15 *requires* setting
`ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM=true` | choice of key-transport algorithm |
+| `ENC_SYM_ALGO` *("encryptionSymAlgorithm")* | AES-128 (CBC by default)
*(documented: `src/site/asciidoc/config.adoc`)* | **maintainer ruling
required** — best-practice doc says "avoid using a cbc Symmetric Encryption
Algorithm" *(inferred — §14 Q11)* | symmetric encryption algorithm |
+| `ENC_MGF_ALGO` | `mgfsha1` *(documented)* | tunable; SHA-1 is fine in MGF1
context but raises scanner noise | MGF for RSA-OAEP |
+| `SIG_DIGEST_ALGO` (default) | `SHA-1` *(documented:
`src/site/asciidoc/config.adoc`)* | **maintainer ruling required** — SHA-1
signatures are below modern guidance *(inferred — §14 Q11)* | default signature
digest |
+| `SIG_ALGO` | per certificate *(documented)* | best-practice doc recommends
explicit `SIG_ALGO` on inbound to bind algorithm | inbound algorithm
enforcement |
+| `SIG_SUBJECT_CERT_CONSTRAINTS` / `SIG_ISSUER_CERT_CONSTRAINTS` | unset
*(documented: `src/site/asciidoc/best_practice.adoc` §"Use Subject DN regular
expressions with chain trust")* | strongly recommended when using chain trust |
regex constraints over signing-cert DNs |
+| `SIG_CERT_CONSTRAINTS_SEPARATOR` | `,` *(documented since 2.2.3)* | tunable
| separator in the constraint string |
+| `ENABLE_REVOCATION` | `false` *(documented)* | optional CRL checking |
inbound certificate revocation |
+| `ENABLE_SIGNATURE_CONFIRMATION` | `false` *(documented)* | tunable | enables
WS-Security SignatureConfirmation flow |
+| `VALIDATE_SAML_SUBJECT_CONFIRMATION` | `true` *(documented)* | hardened |
inbound SAML SubjectConfirmation validation |
+| `NONCE_CACHE_INSTANCE` / `TIMESTAMP_CACHE_INSTANCE` /
`SAML_ONE_TIME_USE_CACHE_INSTANCE` | `EHCacheReplayCache` *(documented)* |
replay protection on by default | replay cache for UsernameToken nonces,
Timestamps, SAML OneTimeUse |
+| `PASSWORD_ENCRYPTOR_INSTANCE` | `JasyptPasswordEncryptor` *(documented)* |
tunable | decryption of encrypted passwords in Crypto properties files |
+| `merlin.keystore.password` | `"security"` *(documented:
`src/site/asciidoc/config.adoc`)* | **maintainer ruling required** — this is a
*default for the property file*; production deployments override *(inferred —
§14 Q12)* | Merlin keystore password |
+| `merlin.truststore.password` | `"changeit"` *(documented)* | dev default |
Merlin truststore password |
+| `merlin.load.cacerts` | `false` *(documented)* | hardened-by-default;
flipping admits the JDK cacerts roots | whether the JDK cacerts are accepted as
trust anchors |
+
+### The insecure-default case
+
+Several knobs above ship with defaults that are *correct for a secure
+production deployment* (`ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM=false`,
+`IS_BSP_COMPLIANT=true`, `TIMESTAMP_STRICT=true`,
+`VALIDATE_SAML_SUBJECT_CONFIRMATION=true`,
+`HANDLE_CUSTOM_PASSWORD_TYPES=false`,
+`ALLOW_USERNAMETOKEN_NOPASSWORD=false`,
+`merlin.load.cacerts=false`). A small number ship in a posture that
+the best-practice document *itself flags as deprecated or weak*:
+default `SIG_DIGEST_ALGO=SHA-1`, default `ENC_SYM_ALGO=AES-128-CBC`,
+`REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS=false`. The maintainer ruling
+on each is captured in §14 Q10–Q11.
+
+## §6 Assumptions about inputs
+
+### Per-entry-point trust table
+
+| Entry point | Parameter | Attacker-controllable? | Caller must enforce |
+| --- | --- | --- | --- |
+| `WSSecurityEngine.processSecurityHeader(Document doc, ...)` (DOM) | `doc`
bytes (the entire SOAP envelope as a parsed DOM) | **yes** — wire-supplied |
parse with a DOM parser that has DTD / XXE disabled (WSS4J does not re-parse) |
+| `WSSecurityEngine.processSecurityHeader(...)` (DOM) | `WSSConfig` /
`RequestData` | **no** — caller-supplied | none |
+| `WSSecurityEngine.processSecurityHeader(...)` (DOM) | `Crypto` (signature
verification trust source) | **no** — caller-supplied | caller controls the
TrustStore contents |
+| `WSSecurityEngine.processSecurityHeader(...)` (DOM) | `CallbackHandler`
(passwords / decryption keys) | **no** — caller-supplied | none |
+| `InboundWSSec.processInMessage(XMLStreamReader xmlStreamReader, ...)` (StAX)
| `xmlStreamReader` | **yes** — wraps wire input | `SUPPORT_DTD=false`,
`IS_SUPPORTING_EXTERNAL_ENTITIES=false`, `IS_COALESCING=false`, and
`WstxInputProperties.P_MIN_TEXT_SEGMENT` per the documented warning
*(documented: `ws-security-stax/.../InboundWSSec.java` line 94-99)* |
+| `InboundWSSec.processInMessage(...)` (StAX) | `WSSSecurityProperties`,
`SecurityEventListener` | **no** — caller-supplied | none |
+| `WSHandler.doSenderAction / doReceiverAction` (action-based) |
`WSHandlerConstants.ACTION` properties | **no** — caller-supplied | must
specify `SIGNATURE_PARTS`, `ENCRYPTION_PARTS`, `OPTIONAL_SIGNATURE_PARTS`,
`SIG_ALGO`, `ENC_SYM_ALGO` per `best_practice.adoc` |
+| `WSSec.loadWSSecuritySchemas` (internal) | XML Schema XSDs / DTDs | **no** —
bundled in jar | n/a; resolver returns bundled `schemas/*.xsd` only
*(documented: `ws-security-stax/.../setup/WSSec.java` lines 463-490)* |
+| Inbound `wsse:UsernameToken` | UT bytes (Username, Password, Nonce, Created,
…) | **yes** | UsernameTokenValidator checks `PASSWORD_TYPE`, nonce replay,
`Created` window per `TTL_USERNAMETOKEN` |
+| Inbound `wsu:Timestamp` | Timestamp bytes (`Created`, `Expires`) | **yes** |
TimestampValidator checks `TIMESTAMP_STRICT`, replay cache,
`TTL_FUTURE_TIMESTAMP` |
+| Inbound `ds:Signature` over message parts | Signature element + the parts it
references | **yes** | Caller must set `SIGNATURE_PARTS` /
`REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS` or use WS-SecurityPolicy to defend
against signature-wrapping |
+| Inbound `xenc:EncryptedData` / `xenc:EncryptedKey` | encrypted payload +
algorithm declarations | **yes** | `ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM=false`,
an `AlgorithmSuite` constrained policy, Bleichenbacher defense in WSS4J |
+| Inbound `saml:Assertion` / `saml2:Assertion` | SAML assertion bytes |
**yes** | Caller-supplied truststore;
`VALIDATE_SAML_SUBJECT_CONFIRMATION=true`; OpenSAML schema processing |
+| Inbound `wsse:BinarySecurityToken` (X.509, Kerberos) | base64 token bytes |
**yes** | Trust chain in `Merlin` truststore; Kerberos validated against KDC |
+| `Crypto` properties file (`org.apache.wss4j.crypto.merlin.*`) | file path,
keystore type, passwords | **no** — caller-supplied | filesystem permissions |
+| SAML `CallbackHandler` (outbound assertion construction) | populated
`SAMLCallback` | **no** — caller-supplied | caller is responsible for the
contents of the assertion |
+| Attachment via `AttachmentRequestCallback` (SwA Profile 1.1, 2.0.0+) |
attachment bytes + headers | **yes** — wire-supplied |
`STORE_BYTES_IN_ATTACHMENT` + signature/encryption parts; *(documented:
`src/site/asciidoc/attachments.adoc`)* |
+| `EXPAND_XOP_INCLUDE` | configuration | **no** | per
`src/site/asciidoc/config.adoc`; inbound expansion default is `true` so signed
bytes (not just XOP refs) are what gets verified |
+
+### Size / shape / rate
+
+- WSS4J accepts arbitrarily large SOAP envelopes through the DOM engine,
+ bounded only by host memory. The streaming engine is bounded by the
+ caller's `XMLInputFactory` limits (e.g. Woodstox `P_MAX_*`)
+ *(documented: `src/site/asciidoc/streaming.adoc`)*.
+- The streaming engine *does* enforce a maximum-decompressed-bytes
+ bound on signed-data compression
+ *(documented: `ws-security-stax/.../wss-config-compression.xml`,
+ exercised by `VulnerabliltyVectorsDecompressedBytesTest`)*.
+- WSS4J has **no rate limiter** of its own — concurrent inbound
+ message rate is whatever the SOAP stack admits.
+
+## §7 Adversary model
+
+### Actors
+
+| Actor | In scope? | Capabilities |
+| --- | --- | --- |
+| **Unauthenticated wire peer** sending a crafted SOAP envelope to the SOAP
stack | **yes** | bytes of the SOAP envelope, including the `wsse:Security`
header and any signed/encrypted body parts |
+| **Authenticated wire peer with valid UsernameToken / X.509 / SAML / Kerberos
credential** | **yes** | as above, plus credentials that pass §8 P1; relevant
for replay / token-reuse / sender-vouches escalation |
+| **Local code in the embedding JVM** | **out of scope** | already in the
process; can read keys directly |
+| **Owner of the keystore / truststore / passwords file** | **out of scope** |
by construction (§3 item 9) |
+| **Author of a hostile `CallbackHandler` / `Validator` plugged into
`WSSConfig`** | **out of scope** | the embedding stack chose to plug it in;
trusted by §2 |
+| **Author of a hostile SAML `CallbackHandler` (outbound) populating arbitrary
claims** | **out of scope** | caller's choice; WSS4J just signs what the
callback returns |
+| **Co-tenant on the same JVM** (e.g. multi-app servlet container) | **out of
scope** *(inferred — §14 Q13)* — Java's `SecurityManager` is the boundary if
any; WSS4J does not claim cross-tenant isolation |
+| **Side-channel observer** (cache timing, branch prediction, EM) | **out of
scope** *(inferred — §14 Q14)* — but constant-time comparison is used for
password / signature-value equality (WSS-677) *(documented: `ChangeLog.txt`
2.3.1)* |
+| **Quantum adversary** | **out of scope** |
+| **Hostile peer impersonating a trusted CA** | covered iff
`SIG_SUBJECT_CERT_CONSTRAINTS` / `SIG_ISSUER_CERT_CONSTRAINTS` are set per
`best_practice.adoc` |
+| **Operator who fails to set the `best_practice.adoc` knobs** | **out of
scope** — operator misconfiguration is documented as such |
+
+## §8 Security properties the project provides
+
+### P1 — Authentication of an inbound SOAP message via the configured
WS-Security tokens
+
+- **Condition**: at least one inbound action is configured; the
+ corresponding `Validator` is registered (default validator set, or
+ caller-supplied); the `Crypto`/TrustStore is configured; the
+ `CallbackHandler` returns the right passwords / keys.
+- **Violation symptom**: an inbound SOAP envelope is accepted as
+ authenticated even though the embedded UsernameToken / X.509
+ signature / SAML assertion / Kerberos token is invalid (wrong
+ signature, unknown CA, untrusted issuer, expired, mis-bound subject).
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented: `src/site/asciidoc/what.adoc` §"Message Authentication";
+ `src/site/asciidoc/topics.adoc` §"Introducing Validators")*
+
+### P2 — Integrity of signed message parts (XML Signature)
+
+- **Condition**: outbound side specified `SIGNATURE_PARTS`; inbound
+ side either uses WS-SecurityPolicy to enforce which parts must be
+ signed, or sets `REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS=true` and
+ specifies `SIGNATURE_PARTS` *(documented:
+ `src/site/asciidoc/best_practice.adoc`)*.
+- **Violation symptom**: an inbound envelope is accepted as integrity-
+ protected even though a referenced part was relocated, swapped, or
+ unsigned (classic XML signature wrapping). This is the precise threat
+ the `VulnerabliltyVectorsTest` regression suite exercises
+ *(documented:
+ `ws-security-policy-stax/src/test/java/.../VulnerabliltyVectorsTest.java`)*.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented)*
+
+### P3 — Confidentiality of encrypted message parts (XML Encryption)
+
+- **Condition**: caller specified `ENCRYPTION_PARTS`, key-transport
+ algorithm not RSA v1.5 (per default), symmetric algorithm an
+ AEAD or operator-accepted CBC, `EncryptedKey` references a key the
+ recipient holds in their decryption keystore.
+- **Violation symptom**: an attacker recovers plaintext of an
+ `EncryptedData` block without the corresponding private key, or
+ bypasses confidentiality by swapping/wrapping `EncryptedData`
+ references.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented)*
+
+### P4 — Defense against Bleichenbacher-style oracle attacks on RSA key
transport
+
+- **Condition**: WSS4J 2.0.0+ with `ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM=false`
+ (the default); upstream Santuario / JCE provider not contributing an
+ oracle.
+- **Violation symptom**: the recipient produces a distinguishable error
+ / timing signal between "padding malformed" and "padding well-formed
+ but plaintext rejected" that lets the attacker iteratively recover the
+ symmetric key.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented: CVE-2015-0226 advisory; `best_practice.adoc`
+ §"Use RSA-OAEP for the Key Transport Algorithm")*
+
+### P5 — Replay protection on inbound UsernameToken nonces, Timestamps, and
SAML2 OneTimeUse assertions
+
+- **Condition**: replay-cache is enabled (default `EHCacheReplayCache`);
+ inbound message carries the required `wsu:Created` / `Nonce` /
+ SAML2 `OneTimeUse` condition.
+- **Violation symptom**: an inbound envelope identical to one already
+ accepted within the TTL window is accepted again.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented: `src/site/asciidoc/config.adoc` §"Non-boolean configuration
+ tags" — `NONCE_CACHE_INSTANCE`, `TIMESTAMP_CACHE_INSTANCE`,
+ `SAML_ONE_TIME_USE_CACHE_INSTANCE`)*
+
+### P6 — Strict Timestamp expiry enforcement
+
+- **Condition**: `TIMESTAMP_STRICT=true` (default); host clock within
+ `TTL_FUTURE_TIMESTAMP` of the sender.
+- **Violation symptom**: an inbound envelope with an `Expires` in the
+ past is accepted.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented: `src/site/asciidoc/config.adoc`)*
+
+### P7 — Basic Security Profile 1.1 compliance on inbound envelopes
+
+- **Condition**: `IS_BSP_COMPLIANT=true` (default since 1.6).
+- **Violation symptom**: an inbound envelope that violates a BSP rule
+ not on the caller's `ignoredBSPRules` list is accepted.
+- **Severity**: **security-critical** when the rule defended a known
+ interop / attack class; **correctness-only** for purely stylistic
+ rules — case-by-case in `BSPRule`.
+- *(documented: `src/site/asciidoc/topics.adoc` §"Basic Security Profile
+ 1.1 compliance")*
+
+### P8 — Constant-time comparison of password / signature secrets
+
+- **Condition**: WSS4J 2.3.1+ for the comparator fix
+ *(documented: `ChangeLog.txt` WSS-677, 2.3.1)*.
+- **Violation symptom**: an attacker recovers a password / signature
+ value via a timing side-channel measured against WSS4J's comparator.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented)*
+
+### P9 — Strict SAML SubjectConfirmation validation
+
+- **Condition**: `VALIDATE_SAML_SUBJECT_CONFIRMATION=true` (default).
+- **Violation symptom**: an inbound SAML assertion with mis-bound
+ Subject / HolderOfKey / Bearer / SenderVouches is accepted as
+ authenticating a different principal.
+- **Severity**: **security-critical**, `VALID` per §13.
+- *(documented: `src/site/asciidoc/config.adoc`)*
+
+### P10 — Decompression-bound enforcement on signed compressed payloads (StAX)
+
+- **Condition**: streaming engine, `wss-config-compression.xml` configures
+ a maximum byte count.
+- **Violation symptom**: an inbound signed compressed payload expands
+ beyond the configured threshold and the engine still accepts it.
+- **Severity**: **security-critical** for DoS resistance,
+ `VALID-HARDENING` boundary case in DOM mode
+ *(inferred — §14 Q15)*.
+- *(documented:
+
`ws-security-stax/src/test/java/.../VulnerabliltyVectorsDecompressedBytesTest.java`)*
+
+## §9 Security properties the project does *not* provide
+
+State each plainly so a triager can route an inbound report to the
+matching disclaimer.
+
+- **No XXE / DTD defense on the bytes-to-DOM step.** WSS4J accepts a
+ pre-parsed `Document` (DOM engine) or `XMLStreamReader` (StAX engine).
+ If the caller hands in a reader / document built with DTD or external
+ entity expansion enabled, WSS4J does not retroactively re-secure it.
+ The streaming engine warns about this in javadoc and configures *its
+ internal* `XML_INPUT_FACTORY` securely *(documented:
+ `ws-security-stax/.../InboundWSSec.java` lines 63-67, 94-99)*.
+- **No transport-layer security.** WSS4J operates on already-received
+ bytes; TLS is the SOAP stack's / container's responsibility
+ *(documented: not the topic of WSS4J — message-level only)*.
+- **No defender against the SOAP stack.** If the SOAP stack routes a
+ body to a service implementation regardless of WSS4J's verdict, that
+ is the stack's bug, not WSS4J's.
+- **No automatic signature-wrapping defense without WS-SecurityPolicy or
+ explicit `SIGNATURE_PARTS`.** *(documented:
+ `src/site/asciidoc/best_practice.adoc` §"Use WS-SecurityPolicy to
+ enforce security requirements")*. The action-based API requires the
+ caller to specify which parts must be signed; failing to specify them
+ produces a working but insecure deployment.
+- **No defense against an authenticated peer who is *also* authorized
+ to do the operation.** WSS4J authenticates the peer and validates the
+ signature; whether the peer is allowed to do what the SOAP body says
+ is the embedding application's authorization decision.
+- **No bound on the size of an inbound SOAP envelope in DOM mode.**
+ DoS via a 10 GB signed envelope is a deployment-side concern; the
+ StAX engine streams, but the DOM engine reads the whole envelope into
+ memory.
+- **No defense against decompression bombs in DOM mode.** Only the
+ streaming engine wires up `MaximumAllowedDecompressedBytes` *(documented:
+ `VulnerabliltyVectorsDecompressedBytesTest`)*.
+- **No data-at-rest protection.** Keystores, truststores, and the
+ EHCache replay-cache spool are read and written through standard JDK /
+ EHCache APIs; encryption of these files at rest is the operator's
+ job.
+- **No constant-time guarantees beyond `MessageDigest.isEqual` since
+ 2.3.1.** Other comparators may still be early-exit; only
+ `UsernameTokenValidator`'s password compare was hardened
+ *(documented: `ChangeLog.txt` WSS-677; *(inferred — §14 Q16)* for
+ the rest)*.
+- **No defense against side-channel observation** of cryptographic
+ operations (cache, branch prediction, EM) beyond what the underlying
+ JCE / Santuario / BC provider provides *(inferred — §14 Q14)*.
+- **No defense against a hostile `CallbackHandler`, `Validator`,
+ `Crypto`, or `Processor` registered through `WSSConfig`.**
+- **No quantum-resistance.**
+
+### False-friend properties (call out separately)
+
+- **`PASSWORD_DIGEST` looks like password hashing, but it is **not**.**
+ The wire-format is `SHA-1(nonce || created || password)`; a peer with
+ the digest and the cleartext nonce + created can mount an offline
+ dictionary attack against the password. *(documented:
+ `src/site/asciidoc/config.adoc` §"PASSWORD_TYPE"; OASIS UsernameToken
+ Profile 1.1)*. Use TLS *and* a strong password policy regardless.
+- **CRC / checksum-like elements in BSP / WS-Security profile are not
+ MACs.** A `wsse:Nonce` is for replay defense, not message integrity.
+- **The Web UI `wsse:Username` / `wsse:Password` text are not
+ authorization tokens.** Any caller-supplied content; authorization is
+ layered on by the embedding stack.
+- **`enableSignatureConfirmation` does not authenticate the *responder*
+ to the original requester** beyond the signature it confirms. It is
+ a replay-style defense, not an extra factor.
+- **A successful X.509 trust chain validation does not authenticate the
+ *holder of the private key* to be the *expected* principal unless
+ `SIG_SUBJECT_CERT_CONSTRAINTS` (or equivalent) is set.** Any cert from
+ any CA in the truststore would otherwise pass *(documented:
+ `src/site/asciidoc/best_practice.adoc` §"Use Subject DN regular
+ expressions with chain trust")*.
+- **`merlin.keystore.password` default of `"security"` and
+ `merlin.truststore.password` default of `"changeit"` are *property-file
+ defaults for documentation*, not WSS4J runtime defaults for production
+ keystores** — the actual keystore on disk has whatever password the
+ operator gave it *(documented: `src/site/asciidoc/config.adoc`)*.
+- **The `ws-security-web` servlet filter is not a SOAP stack.** It does
+ not parse the SOAP envelope or route bodies; it is a thin entrypoint
+ *(inferred — §14 Q3)*.
+- **Apache Santuario's `setSecureValidation` setting does not subsume
+ WSS4J's `REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS`.** They cover
+ different attack classes *(inferred — §14 Q10)*.
+
+### Well-known attack classes the project does not single-handedly defend
against
+
+- **XML Signature wrapping** — defended only with WS-SecurityPolicy or
+ explicit `SIGNATURE_PARTS` + `REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS`.
+- **XXE / billion laughs** — relies on caller's parser configuration.
+- **Compression bomb in `EncryptedData`** — only the streaming engine
+ enforces a bound by default.
+- **Bleichenbacher / oracle on RSA v1.5 key transport** — defended only
+ with the default `ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM=false`.
+- **CBC padding oracle on symmetric encryption** — `best_practice.adoc`
+ advises switching to AES-GCM; default of AES-CBC is still in use
+ *(inferred — §14 Q11)*.
+- **Timing side-channel on token comparators** — `MessageDigest.isEqual`
+ is used since 2.3.1 in `UsernameTokenValidator`; comprehensive sweep
+ is *(inferred — §14 Q16)*.
+- **SAML assertion forgery via signature wrapping or sender-vouches
+ misuse** — defended via OpenSAML + `VALIDATE_SAML_SUBJECT_CONFIRMATION`.
+- **Token replay** — defended via the three replay caches.
+
+## §10 Downstream responsibilities
+
+The embedding SOAP stack / application **must**:
+
+1. Parse the inbound SOAP bytes with a DOM parser or `XMLStreamReader`
+ configured with DTDs disabled and external entities disabled. The
+ streaming engine's javadoc spells out the minimum:
+ `SUPPORT_DTD=false`, `IS_SUPPORTING_EXTERNAL_ENTITIES=false`,
+ `IS_COALESCING=false`, and (for Woodstox) `P_MIN_TEXT_SEGMENT=8192`
+ *(documented: `ws-security-stax/.../InboundWSSec.java` lines 94-99)*.
+2. Use WS-SecurityPolicy when at all possible, in preference to the
+ action-based approach *(documented:
+ `src/site/asciidoc/best_practice.adoc` §"Use WS-SecurityPolicy")*.
+3. When using the action-based approach, set `SIGNATURE_PARTS` and
+ `ENCRYPTION_PARTS` for outbound, and
`REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS=true`
+ for inbound; specify `SIG_ALGO` and `ENC_KEY_TRANSPORT` on inbound to
+ enforce algorithm choice *(documented:
+ `src/site/asciidoc/best_practice.adoc`)*.
+4. Never set `ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM=true` for new
+ deployments *(documented: `src/site/asciidoc/best_practice.adoc`,
+ CVE-2015-0226)*.
+5. Prefer AES-GCM symmetric algorithms over CBC by explicitly setting
+ `ENC_SYM_ALGO` to a `xmlenc11#aes*-gcm` identifier *(documented:
+ `src/site/asciidoc/best_practice.adoc` §"Avoid using a cbc Symmetric
+ Encryption Algorithm")*.
+6. When relying on chain trust, set `SIG_SUBJECT_CERT_CONSTRAINTS` (or
+ `SIG_ISSUER_CERT_CONSTRAINTS`) to bound the acceptable signer
+ identities *(documented:
+ `src/site/asciidoc/best_practice.adoc` §"Use Subject DN regular
+ expressions with chain trust")*.
+7. Restrict OS-level permissions on keystore / truststore / Crypto
+ `.properties` files to the running process owner.
+8. Configure the EHCache replay-cache to a writable, persistent location
+ sized for the expected token-per-TTL volume; verify that the cache
+ is durable across host restart if the deployment relies on cross-
+ restart replay defense *(inferred — §14 Q17)*.
+9. Run on a supported WSS4J branch (4.0.x / 3.0.x / 2.4.x) *(documented:
+ `SECURITY.md`)*.
+10. Use TLS for the transport — WSS4J is message-level security, not
+ a substitute for transport security.
+11. Treat the `CallbackHandler` and any custom `Validator` as part of
+ the security TCB.
+12. Set `ENABLE_REVOCATION=true` and supply a current `merlin.x509crl.file`
+ when revocation matters *(documented: `src/site/asciidoc/config.adoc`)*.
+13. Override `merlin.keystore.password` / `merlin.truststore.password`
+ away from the documented `"security"` / `"changeit"` defaults
+ *(documented)*.
+14. Never enable `merlin.load.cacerts=true` unless the embedding
+ application is willing to trust the system-wide CA bundle for
+ WS-Security purposes *(documented)*.
+15. Apply security advisories listed at
+ `http://ws.apache.org/wss4j/security_advisories.html` and at
+ Apache CXF's advisory page (many WS-Security CVEs land there
+ rather than on WSS4J) *(documented:
+ `src/site/xdoc/security_advisories.xml`)*.
+
+## §11 Known misuse patterns
+
+- **Using WSS4J with the action-based approach and forgetting to set
+ `SIGNATURE_PARTS` on the receiving side.** Inbound signature is
+ validated, but the validated set may be empty or only the SOAP body,
+ letting a signature-wrapping attacker move signed content into a
+ header *(documented: `src/site/asciidoc/best_practice.adoc`)*.
+- **Plumbing a wire-supplied `XMLStreamReader` into `InboundWSSec`
+ without disabling DTD / external entities** — the engine itself only
+ configures its *internal* factory *(documented:
+ `ws-security-stax/.../InboundWSSec.java` warning)*.
+- **Trusting an X.509 chain-validated certificate without
+ `SIG_SUBJECT_CERT_CONSTRAINTS`.** Any cert any CA in the truststore
+ issued is admitted *(documented:
+ `src/site/asciidoc/best_practice.adoc`)*.
+- **Re-enabling RSA v1.5 (`ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM=true`)
+ for interop with a legacy peer.** Re-introduces the Bleichenbacher
+ oracle path even though WSS4J's own defense exists.
+- **Reusing the same `merlin.keystore.password` (`"security"` from the
+ documented example) in production deployments.**
+- **Disabling BSP compliance via `IS_BSP_COMPLIANT=false` to "make
+ interop work" with a non-conformant stack.**
+- **Treating `PASSWORD_DIGEST` as offline-attack-resistant.** Combined
+ with a weak password, the digest can be brute-forced offline
+ *(documented: `src/site/asciidoc/config.adoc` and OASIS UT 1.1)*.
+- **Building a custom `Validator` that returns success on the wrong
+ branch** — e.g. validating the literal text of a SAML assertion
+ without checking enveloped signature on the OpenSAML 1.x → 2.x
+ upgrade boundary *(documented: `src/site/asciidoc/topics.adoc`
+ §"Support for SAML2 assertions" — WSS-146)*.
+- **Setting `ENABLE_REVOCATION=true` without supplying a CRL file** —
+ validation silently degrades when the CRL is missing
+ *(inferred — §14 Q18)*.
+- **Using the same `nonceCacheInstance` across deployments at different
+ trust levels** — a leak in deployment A becomes a forgery primitive
+ in deployment B *(inferred — §14 Q17)*.
+- **Mixing the action-based and WS-SecurityPolicy approaches in the
+ same handler chain.** The behavior across both is documented but
+ rarely tested.
+
+## §11a Known non-findings (recurring false positives)
+
+This section is the highest-leverage input for automated agentic
+security scans. Each entry: tool symptom, why it is safe under the
+model, the section that licenses the call.
+
+- **"`MessageDigest.MD5` / `MessageDigest.SHA1` instantiated in
+ `Merlin`-related code."** `SHA1` is used for legacy
+ digestMethod identifiers and OASIS-specified token digests; MD5 is
+ used for OASIS-specified `XKMS` legacy identifiers. WSS4J's
+ algorithm choice for *production signature/encryption* is controlled
+ by `SIG_DIGEST_ALGO` / `SIG_ALGO` / `ENC_SYM_ALGO`. → `BY-DESIGN:
+ property-disclaimed` per §9 *(inferred — §14 Q11)*.
+- **"Hardcoded `'security'` / `'changeit'` / `'default'` keystore
+ password in test resources."**
`ws-security-dom/src/test/resources/*.properties`
+ and `ws-security-stax/src/test/resources/*.jks` are test data;
+ documented passwords. → `OUT-OF-MODEL: unsupported-component` per §3
+ item 10.
+- **"Hardcoded `'security'` default in
+ `src/site/asciidoc/config.adoc`."** Documentation default for the
+ `merlin.keystore.password` property file format; not a runtime
+ default. → `KNOWN-NON-FINDING`.
+- **"`XMLInputFactory.newInstance()` in
+ `ws-security-stax` does not call `setProperty(SUPPORT_DTD, false)`."**
+ Check `InboundWSSec`'s static initializer: it does
+ *(documented: `InboundWSSec.java` lines 63-67)*. The instances *callers
+ supply* are the caller's concern per the documented warning.
+- **"`DocumentBuilderFactory` not hardened in `ws-security-dom`."** The
+ DOM engine does not parse XML bytes; it receives a `Document` from
+ the SOAP stack. → `OUT-OF-MODEL: trusted-input` per §3 item 2.
+- **"Path traversal via `merlin.keystore.file`."** Caller-supplied
+ property. → `OUT-OF-MODEL: trusted-input`.
+- **"Plaintext password in `Crypto` properties file."** A
+ `PasswordEncryptor` (default `Jasypt`) is documented; passwords *can*
+ be encrypted in the properties file *(documented:
+ `src/site/asciidoc/config.adoc` `PASSWORD_ENCRYPTOR_INSTANCE`)*. A
+ literal plaintext password is a deployment choice. → `OUT-OF-MODEL:
+ trusted-input`.
+- **"`Arrays.equals` early-exit in
+ `org.apache.wss4j.dom.transform.STRTransform` / signature value
+ compare."** WSS-677 hardened `UsernameTokenValidator`; if a scanner
+ flags a different comparator, treat as `MODEL-GAP` and trigger §12
+ *(inferred — §14 Q16)*.
+- **"`AccessController.doPrivileged` block."** The legacy SecurityManager
+ bracket is a JDK requirement on JDK pre-17. Reports against this
+ pattern are `OUT-OF-MODEL: non-default-build` *(inferred — §14 Q5)*.
+- **"`RuntimeException` thrown from `Crypto` property loader on bad
+ password."** The exception is the documented failure mode; not a
+ crash leak. → `BY-DESIGN: property-disclaimed`.
+- **"Open `KeyStore` and silently fall back to default on missing
+ file."** Reproducible only under operator misconfiguration. →
+ `OUT-OF-MODEL: trusted-input`.
+- **"`opensaml-*.jar` has CVE-X."** Report upstream to the OpenSAML
+ project; WSS4J picks up via dependency bump
+ *(documented: `ChangeLog.txt` WSS-687 et al)*. → `OUT-OF-MODEL:
+ unsupported-component`.
+- **"DTD / billion-laughs against WSS4J DOM engine via crafted
+ `<soap:Envelope>`."** WSS4J does not parse bytes-to-DOM; the SOAP
+ stack does. → `OUT-OF-MODEL: trusted-input` per §3 item 2.
+- **"`InputStream.close()` not in finally."** Code-quality finding,
+ not a security one. → `OUT-OF-MODEL: out-of-layer`.
+
+## §12 Conditions that would change this model
+
+Revise this document when any of the following lands:
+
+- A new WS-Security profile or token type (e.g. a new bearer-token
+ profile).
+- A new processing engine beyond the DOM and StAX implementations.
+- A change in the default value of any §5a knob — especially
+ `ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM`, `IS_BSP_COMPLIANT`,
+ `ENC_KEY_TRANSPORT`, `ENC_SYM_ALGO`, `SIG_DIGEST_ALGO`,
+ `VALIDATE_SAML_SUBJECT_CONFIRMATION`.
+- An upgrade of the bundled OpenSAML / Apache Santuario / Bouncy Castle
+ / EHCache that materially changes default behavior.
+- A new public extension point (`Validator`, `Processor`,
+ `CallbackHandler` subclass) that materially expands the trusted-by-
+ construction set.
+- A new bound-violation surface, e.g. shipping a built-in maximum
+ envelope size on the DOM engine.
+- A vulnerability report that cannot be cleanly routed to one of the
+ §13 dispositions: that is evidence the model has a gap.
+
+## §13 Triage dispositions
+
+A report against WSS4J receives exactly one of the following:
+
+| Disposition | Meaning | Licensed by |
+| --- | --- | --- |
+| `VALID` | Violates a §8 property via an in-scope §7 adversary using an
in-scope §6 input on a supported branch per `SECURITY.md`. | §8, §6, §7 |
+| `VALID-HARDENING` | No §8 property violated, but a §11 misuse pattern can be
made harder to fall into by code change. Typically no CVE. | §11 |
+| `OUT-OF-MODEL: trusted-input` | Requires attacker control of a §6 parameter
the model marks trusted (caller-supplied `XMLStreamReader` not hardened against
XXE, caller-supplied `Crypto` properties file path, hostile `CallbackHandler`,
…). | §6 |
+| `OUT-OF-MODEL: adversary-not-in-scope` | Requires a §7 actor the model
excludes (in-process attacker, owner of the keystore, hostile SOAP stack). | §7
|
+| `OUT-OF-MODEL: unsupported-component` | Lands in `*/src/test/`,
`integration/`, vendored sample data, or in upstream code (OpenSAML, Santuario,
EHCache, BC, JCE). | §3 items 3–7, §3 item 10 |
+| `OUT-OF-MODEL: non-default-build` | Only manifests when a §5a knob the
maintainer rules dev/test has been flipped (e.g.
`ALLOW_RSA15_KEY_TRANSPORT_ALGORITHM=true`, `IS_BSP_COMPLIANT=false`,
`TIMESTAMP_STRICT=false`). | §5a |
+| `BY-DESIGN: property-disclaimed` | Concerns a §9 property the project
explicitly does not provide (no XXE defense on caller-parsed XML, no automatic
signature-wrapping defense without WS-SecurityPolicy or `SIGNATURE_PARTS`, no
rate limiting, no data-at-rest encryption). | §9 |
+| `KNOWN-NON-FINDING` | Matches a §11a recurring false positive. | §11a |
+| `MODEL-GAP` | Cannot be cleanly routed to any of the above — triggers §12
model revision. | §12 |
+
+## §14 Open questions for the maintainers
+
+Every *(inferred)* tag in the body maps to one of these. Proposed
+answers are inline; please confirm, correct, or strike.
+
+### Wave 1 — scope, trust boundaries
+
+**Q1.** Confirm that a `CallbackHandler` and a `Validator` plugged into
+`WSSConfig` are treated as part of WSS4J's trusted computing base — i.e.
+a hostile callback / validator is out of model (proposed: **yes**, §2
+caller-roles, §3 item 9). *(maps to §2, §3, §7)*
+
+**Q2.** Defects intrinsic to Apache Santuario, OpenSAML, Bouncy Castle,
+EHCache, and the JDK JCE provider — confirm policy is "report upstream;
+WSS4J picks up via dependency bump" (proposed: **yes**). *(maps to §3
+items 3–7, §11a)*
+
+**Q3.** `ws-security-web` ships a servlet `Filter` and helper utility.
+Is it (a) a first-class supported entry point, (b) a legacy artifact,
+or (c) sample code best removed? The draft assumes (a) with the caveat
+that the surrounding container is out of model. *(maps to §2, §3 item 1)*
+
+**Q4.** EHCache default replay-cache backing: proposed treatment is
+"EHCache the library is upstream; WSS4J's wiring of it is in-model"
+(WSS-643 NullPointerException example). Confirm? *(maps to §3 item 7)*
+
+**Q5.** Supported JDK matrix per branch: proposed 2.4.x = JDK8+, 3.0.x
+= JDK11+ (Jakarta namespace migration WSS-694), 4.0.x = JDK17+ (per
+`README.md` JDK17 badge). Confirm exact minima. *(maps to §5)*
+
+**Q6.** JCE Unlimited Strength: now default on Java 8u151+, so the
+README requirement is effectively a no-op on supported JDKs. Confirm
+WSS4J makes no claim about behavior on pre-8u151 JDKs (proposed: out
+of model). *(maps to §5)*
+
+**Q7.** Clock-skew assumption: WSS4J's `TTL_FUTURE_TIMESTAMP=60` and
+`TTL_TIMESTAMP=300` are documented defaults; do you make any Impala-
+level claim about host-clock accuracy, or is that entirely operator
+responsibility (proposed: operator)? *(maps to §5, §10)*
+
+**Q8.** Confirm the negative-side inventory in §5: WSS4J opens **no**
+sockets, spawns **no** processes, installs **no** signal handlers, and
+reads only the documented system properties. Anything else? *(maps to §5)*
+
+**Q9.** Confirm WSS4J writes only via SLF4J + the EHCache spool (when
+configured) + the keystore/truststore files (when callers ask
+`Merlin` to write). *(maps to §5)*
+
+### Wave 2 — insecure defaults & enforcement gaps
+
+**Q10.** `REQUIRE_SIGNED_ENCRYPTED_DATA_ELEMENTS` defaults to `false`.
+CVE-2015-0227 was about enforcing this property. Is the production
+guidance "set to `true` per §10" (proposed) or "set explicitly via
+WS-SecurityPolicy so it is policy-enforced"? Is a report against an
+unset deployment `VALID` (operator-error) or `OUT-OF-MODEL: non-default-build`?
+*(maps to §5a, §8 P2, §10, §13)*
+
+**Q11.** **Default SHA-1 signature digest and AES-128-CBC symmetric
+algorithm.** The best-practice document explicitly recommends moving
+off CBC and is silent on the SHA-1 default. Are these defaults
+"supported production posture" (so a report against them is `VALID`),
+"dev-default; operators must flip per §10" (so reports are
+`OUT-OF-MODEL: non-default-build`), or "deprecated but kept for legacy
+interop, will be flipped in a future release" (so reports are
+`VALID-HARDENING`)? Proposed answer for both: **legacy interop default
+— `VALID-HARDENING` with a §10 note**. *(maps to §5a, §10, §11a)*
+
+**Q12.** `merlin.keystore.password=security` and
+`merlin.truststore.password=changeit` are documented as defaults in
+`config.adoc`. Confirm these are *documentation defaults for the
+property-file format*, not runtime defaults applied by WSS4J when the
+property is absent (proposed: documentation only). *(maps to §5a, §9
+false-friend)*
+
+**Q13.** Co-tenant attackers (multiple webapps in one JVM): does WSS4J
+make any claim about cross-classloader / cross-tenant isolation
+(proposed: **no**, §7 out-of-scope)? *(maps to §7, §9)*
+
+**Q14.** Side-channel observers (cache, branch prediction, EM): out of
+scope (proposed)? *(maps to §7, §9)*
+
+**Q15.** §8 P10 (decompression bound) is enforced by the StAX engine
+when wired via `wss-config-compression.xml`. Is the analog in the DOM
+engine an out-of-model concern (proposed: `VALID-HARDENING`), or does
+the DOM engine carry an equivalent? *(maps to §8 P10, §9)*
+
+**Q16.** WSS-677 hardened `UsernameTokenValidator` to use
+`MessageDigest.isEqual`. Has the rest of the comparator surface
+(signature values, SAML attribute matches, BSP rule compares) been
+swept? Proposed §11a treatment is "specific WSS-677 surface is
+hardened; other comparator reports are `MODEL-GAP` until ruled". *(maps
+to §8 P8, §9, §11a)*
+
+**Q17.** Replay-cache durability: do you make a claim that an
+`EHCacheReplayCache` configured with a persistent spool defends
+across-restart (proposed: **yes, when persistent spool is configured**),
+or is replay defense documented as best-effort? *(maps to §5a, §10)*
+
+**Q18.** `ENABLE_REVOCATION=true` without a CRL file: does WSS4J fail
+loudly or silently degrade? Proposed treatment in §11 is "silent
+degrade is a `VALID-HARDENING` for a clearer log line / startup
+exception". *(maps to §11)*
+
+### Wave 3 — meta & coexistence
+
+**Q19.** This document should be hosted in-repo at
+`docs/security/threat-model.md` (proposed) or on
+`ws.apache.org/wss4j/`? *(meta)*
+
+**Q20.** `SECURITY.md` is currently disclosure-only. Should this
+threat model (a) replace its in-the-spirit-of-threat-model content,
+(b) become canonical and `SECURITY.md` link to it, or (c) sit
+alongside as an expansion (proposed: (b))? *(meta — §3.1a)*
+
+**Q21.** `best_practice.adoc` already does much of the §10 work for
+us. Should the threat model link to it as the canonical operator
+guidance, or maintain a separate §10 list (proposed: link + a §10
+table that *references* the best-practice entries)? *(meta)*
+
+**Q22.** §11a known-non-findings is thin (~13 patterns). Could the
+WSS4J / Webservices PMC populate from the JIRA "not a bug" / "wontfix"
+closures (`WSS-*` tickets)? Concrete asks: 3–5 patterns the PMC sees
+recur in inbound reports against WSS4J. *(meta — §11a)*
+
+**Q23.** What kind of change to WSS4J should trigger a revision
+(proposed list in §12 — confirm or correct)? *(meta — §12)*
+
+---
+
+## Appendix: SECURITY.md / website → §x back-map
+
+WSS4J's `SECURITY.md` is disclosure-only — it does not embed
+threat-model content beyond the supported-branch matrix. The other
+documented sources are the AsciiDoc pages under `src/site/asciidoc/`
+(published at `https://ws.apache.org/wss4j/`).
+
+| Source | Claim | Lands in |
+| --- | --- | --- |
+| `SECURITY.md` | Supported branches: 4.0.x, 3.0.x, 2.4.x | §1 supported
branches, §3 item 8, §13 `VALID` precondition |
+| `SECURITY.md` | Reporting via `https://www.apache.org/security/` | §1
reporting |
+| `SECURITY.md` | Advisories at
`http://ws.apache.org/wss4j/security_advisories.html` | §1 reporting, §10 item
15 |
+| `README.md` | WS-Security profiles supported | §1 description, §2 intended
use |
+| `README.md` | Apache Santuario / OpenSAML / BC cryptographic dependencies |
§3 item 3, §3 item 5, §5 environment |
+| `README.md` Test Requirements | JCE Unlimited Strength | §5 environment |
+| `src/site/asciidoc/what.adoc` | Message Confidentiality / Integrity /
Authentication / Authorization | §8 P1–P3 |
+| `src/site/asciidoc/using.adoc` | "Apache WSS4J is designed to be used with a
Web Services stack such as Apache CXF or Apache Axis" | §2 deployment shape, §3
item 1 |
+| `src/site/asciidoc/best_practice.adoc` §"Use WS-SecurityPolicy" | "gives you
more *automatic* protection against various attacks" + link to
signature-wrapping post | §3.1 mining; §8 P2 violation symptom; §9
first/disclaim entry; §10 item 2 |
+| `src/site/asciidoc/best_practice.adoc` §"Use RSA-OAEP" | CVE-2015-0226 /
Bleichenbacher | §8 P4, §10 item 4, §11 |
+| `src/site/asciidoc/best_practice.adoc` §"Avoid using a cbc Symmetric
Encryption Algorithm" | recommend GCM | §5a `ENC_SYM_ALGO` insecure-default,
§10 item 5, §14 Q11 |
+| `src/site/asciidoc/best_practice.adoc` §"Use Subject DN regular expressions"
| recommended with chain trust | §5a `SIG_SUBJECT_CERT_CONSTRAINTS`, §10 item
6, §11 |
+| `src/site/asciidoc/best_practice.adoc` §"Specify signature algorithm on
receiving side" | bind algorithm via `SIG_ALGO` | §6 trust table, §10 item 3 |
+| `src/site/asciidoc/config.adoc` | full configuration-tag inventory | §5a |
+| `src/site/asciidoc/topics.adoc` §"Introducing Validators" | Validator
concept; default validators; signature-wrapping defense | §2 caller-roles, §8
P1, §11a `Validator` non-finding |
+| `src/site/asciidoc/topics.adoc` §"Basic Security Profile 1.1 compliance" |
BSP default is `true` since 1.6 | §8 P7, §5a `IS_BSP_COMPLIANT` |
+| `src/site/asciidoc/topics.adoc` §"WSPasswordCallback identifiers" |
CallbackHandler is the password / key channel | §2 caller-roles, §3 item 9, §6
entry-point table |
+| `src/site/asciidoc/streaming.adoc` | StAX engine introduced in 2.0.0;
limitations vs DOM | §2 component-family table, §4 reachability, §9
(limitations) |
+| `src/site/asciidoc/attachments.adoc` | SwA Profile 1.1 sign/encrypt support
| §6 attachment row |
+| `src/site/asciidoc/wss4j22.adoc` | JDK8 minimum, Kerberos changes | §5 |
+| `src/site/xdoc/security_advisories.xml` | CVE-2015-0226, CVE-2015-0227;
pointer to CXF advisories | §1 reporting, §10 item 15 |
+|
`ws-security-stax/src/main/java/org/apache/wss4j/stax/setup/InboundWSSec.java`
(javadoc + static init) | "configure your xmlStreamReader correctly. Otherwise
you can create a security hole" — internal factory sets DTD/external-entity to
false | §3 item 2, §6 trust table, §10 item 1, §11a `XMLInputFactory`
non-finding |
+| `ws-security-stax/src/main/java/org/apache/wss4j/stax/setup/WSSec.java` |
`SchemaFactory.setFeature(FEATURE_SECURE_PROCESSING, true)` for bundled schema
load | §5 environment |
+|
`ws-security-common/src/main/java/org/apache/wss4j/common/cache/EHCacheReplayCache.java`
| EHCache-backed replay defense | §5a, §8 P5, §10 item 8 |
+|
`ws-security-common/src/main/java/org/apache/wss4j/common/ConfigurationConstants.java`
+ `ws-security-dom/.../RequestData.java` | configuration-tag definitions and
defaults | §5a |
+| `ws-security-policy-stax/src/test/java/.../VulnerabliltyVectorsTest.java` |
SOAPAction spoofing is out of scope; signed-body-relocation is a `VALID`
regression test | §3 item 1, §8 P2 |
+|
`ws-security-stax/src/test/java/.../VulnerabliltyVectorsDecompressedBytesTest.java`
| "Maximum byte count … reached" enforced on signed compressed payloads | §8
P10, §10 item 8 |
+| `ChangeLog.txt` WSS-677 (2.3.1) | "Comparison in validate class is
vulnerable to timing side channels" | §8 P8 |
+| `ChangeLog.txt` WSS-694 (3.0.0) | Move wss4j to native jakarta namespace |
§5 environment |
+| `ChangeLog.txt` WSS-684/WSS-687 (3.0.0) | OpenSAML 4 upgrade | §3 item 5, §5
|
+| `Performance-Tips.txt` | engineering guidance only, not security; called out
so triagers do not mine it | n/a |