From 51cf0f43ad2e0ce3606c85a67b84339b3549a4ba Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Sun, 31 May 2026 03:31:57 +0200 Subject: [PATCH] Add draft THREAT_MODEL.md + AGENTS.md and link from SECURITY.md for security-model discoverability Adds a draft (v0) threat model for the Fediz WS-Federation/SAML-SSO IdP + RP plugins, plus an AGENTS.md, and links the model from the existing SECURITY.md so an automated scan agent can discover it via AGENTS.md -> SECURITY.md -> THREAT_MODEL.md. The model is a proposal for the PMC to review; most claims are (inferred) and route to open questions in its section 14. Generated-by: Claude Code (Claude Opus 4.8) --- AGENTS.md | 33 ++++++ SECURITY.md | 9 ++ THREAT_MODEL.md | 268 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 310 insertions(+) create mode 100644 AGENTS.md create mode 100644 THREAT_MODEL.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..e56a31b96 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,33 @@ + + +# Agent Guide for Apache CXF Fediz + +This file is read by automated agents (security scanners, code analyzers, +AI assistants) operating on this repository. It points them at the +human-authored references they should consult before producing output. + +## Security + +Security model: [SECURITY.md](./SECURITY.md) -> [THREAT_MODEL.md](./THREAT_MODEL.md) + +Agents that scan this repository should consult `SECURITY.md` and the linked +`THREAT_MODEL.md` before reporting issues. Fediz is a WS-Federation / SAML-SSO +system: the security pivot is SAML token validation at the Relying-Party plugin +(signature against a trusted IdP cert, audience/wtrealm, Conditions/timestamps, +replay, anti-signature-wrapping) and token issuance at the IdP (reply-URL +allow-listing, login CSRF/throttling). The token travels through the untrusted +browser, so most properties are about validating it. diff --git a/SECURITY.md b/SECURITY.md index 446602ecf..720ba4332 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -12,3 +12,12 @@ For information on how to report a new security problem please see [here](https://www.apache.org/security/). Our existing security advisories are published [here](http://cxf.apache.org/security-advisories.html). + +## Threat Model + +What Fediz treats as in scope and out of scope, the security properties it +provides and disclaims (RP token validation: signature, audience, conditions, +replay, anti-signature-wrapping; IdP issuance and reply-URL validation), the +adversary model, and how inbound reports and tool/AI findings are triaged are +documented in [THREAT_MODEL.md](./THREAT_MODEL.md). Reporters and triagers +should consult it alongside this policy. diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md new file mode 100644 index 000000000..d6456ed43 --- /dev/null +++ b/THREAT_MODEL.md @@ -0,0 +1,268 @@ + + +# Threat Model — Apache CXF Fediz + +## §1 Header + +- **Project:** Apache CXF Fediz — a web-application **single sign-on** system implementing the + **WS-Federation 1.2 Passive Requestor Profile** (and SAML-SSO), with **SAML 1.1 / 2.0 tokens**, role + information carried as SAML `AttributeStatement`s, and a published WS-Federation metadata document + *(documented — README)*. It has two deployable halves: an **Identity Provider (IdP)** that authenticates + users and issues tokens (STS-backed), and **Relying-Party (RP) plugins** embedded in a protected web app + that redirect unauthenticated users to the IdP and **validate the returned token** *(documented — `services/`, + `plugins/`)*. +- **Modelled against:** `apache/cxf-fediz` `main`/HEAD (2026-05-31). +- **Status:** **DRAFT — v0, not yet reviewed by the CXF PMC.** Companion to the `apache/cxf` umbrella model; + Fediz's SSO trust surface is distinct and modelled here. Produced via the `threat-model-producer` rubric + (). +- **Version binding / reporting / legend / advisories** as in the CXF model + (). **Draft confidence:** ~12 documented / 0 maintainer / + ~46 inferred. Each *(inferred)* routes to §14. + +In SSO the security pivot is **token validation at the RP** and **token issuance at the IdP**, both around a +token that travels through the *untrusted* browser. Most of this model lives in §8/§9 around those two points. + +## §2 Scope and intended use + +Intended use *(documented)*: protect web applications by delegating authentication to a Fediz IdP via +WS-Federation/SAML-SSO; the RP plugin enforces "authenticated + has claims" before the app runs. + +Caller roles: + +- **End-user browser (untrusted)** — drives the redirect-based flow and carries the token between IdP and RP. +- **Relying Party (the protected app + plugin)** — trusts tokens it can validate against the configured IdP. +- **Identity Provider** — authenticates users, issues signed tokens, exposes login + metadata. +- **STS** — issues/validates the underlying SAML tokens (WS-Trust) for the IdP. +- **Deployer/operator** — configures IdP trust (signing certs), RP `wtrealm`/audience, allowed reply URLs, + TLS, and the user/credential store. **Trusted; out of model as adversary (§3).** + +**Component-family table:** + +| Family | Entry point | Touches outside process | In model? | +| --- | --- | --- | --- | +| RP plugin — token processing | `plugins/core` (`saml`, `samlsso`, `processor`, `handler`) | browser-delivered token; IdP cert | **Yes** | +| RP plugin — container integration | tomcat/jetty/spring/websphere plugins | servlet request/session | **Yes** | +| IdP service — auth + token issuance | `services/` (IdP), login | network; user store; STS | **Yes** | +| STS / WS-Trust | STS service | crypto; user store | **Yes** | +| WS-Fed metadata publish | metadata endpoint | network | **Yes** | +| Examples / systests / packaging | `examples/`, `systests/`, `apache-fediz`, `etc/` | — | No → §3 | + +## §3 Out of scope (explicit non-goals) + +- **The operator/deployer as adversary** and pure misconfiguration (wrong IdP cert trust, audience left + unset, TLS disabled) — Fediz provides the validation; configuring it correctly is the deployer's job + (§10/§11) *(inferred)*. +- **The protected application's own logic and its use of the established identity** beyond what the plugin + provides. +- **The user-credential store / external auth backends** the IdP delegates to. +- **`examples/`, `systests/`, packaging, and `etc/` sample configs** *(inferred)*. +- **The underlying CXF/WSS4J XML-signature stack internals** except as Fediz configures and invokes them + (the CXF umbrella model covers those). + +## §4 Trust boundaries and data flow + +Two boundaries dominate: + +1. **RP ← browser-delivered token (the critical one).** After IdP login the browser POSTs/redirects a + `wresult`/SAMLResponse to the RP. These bytes are **fully attacker-influenceable** (the user controls + their browser); the RP must treat the token as untrusted until it has verified: the **XML signature** + against the configured IdP signing certificate; the **issuer**; the **audience/`wtrealm`** equals this RP; + the **conditions/timestamps** (NotBefore/NotOnOrAfter, clock skew); **one-time use / replay**; and that + the signature covers the asserted content (**anti-XSW**) *(inferred — `samlsso`/`processor`; specifics §14)*. +2. **IdP ← login + token request.** The IdP authenticates the user and issues a token bound to the + requesting RP (`wtrealm`) with a reply address (`wreply`); it must validate the RP/realm and the reply + URL (open-redirect / token-forwarding surface) and protect the login from CSRF/brute-force *(inferred)*. + +WS-Fed flow parameters (`wa`, `wresult`, `wctx`, `wtrealm`, `wreply`, `wfresh`, `wreq`) are all +attacker-tamperable in the browser and must be validated, not trusted *(inferred)*. + +**Reachability preconditions:** an RP-side finding is in-model if reachable from a crafted/replayed token or +flow parameter *before* the plugin establishes the security context; an IdP-side finding is in-model if +reachable from an unauthenticated request to login/metadata/token endpoints; misconfiguration-only outcomes +are `OUT-OF-MODEL` (§3/§5a). + +## §5 Assumptions about the environment + +- **Runtime:** JVM web apps in a servlet container (RP plugins integrate with Tomcat/Jetty/Spring/WebSphere) + and a deployed IdP/STS *(documented)*. +- **Trust material:** the RP is configured with the IdP's signing certificate(s)/truststore; the IdP/STS hold + signing keys *(inferred)*. +- **Transport:** TLS protects the browser↔IdP and browser↔RP legs; Fediz supports but the deployer enforces + it *(inferred — wave-1)*. +- **Clocks:** IdP and RP clocks are roughly synchronized for `Conditions` validation (a skew window applies) + *(inferred)*. +- **What Fediz does to its host (*(inferred)* — wave-2):** processes XML/SAML; performs crypto; reads + configured truststores/keystores; the IdP talks to the STS and the user store; not assumed to execute host + commands. + +## §5a Build-time and configuration variants + +| Knob (names *(inferred)*) | Effect | Default ruling needed | +| --- | --- | --- | +| RP signature requirement / trusted IdP certs | Whether unsigned/untrusted tokens are rejected | **Open (wave-1):** signature + trust mandatory by default? | +| Audience (`wtrealm`) restriction enforcement | Prevents token replay to a different RP | **Open (wave-1)** | +| Token replay cache (one-time-use) | Replay protection of consumed tokens | **Open (wave-1)** | +| Clock-skew window for `Conditions` | Forgiveness of timestamp validation | Confirm default | +| `wreply`/reply-URL validation at the IdP | Open redirect / token forwarding | **Open (wave-1)** | +| SAML XML secure-processing (DTD/entity off) | XXE/XML-DoS on token parse | Confirm inherited CXF defaults | +| TLS enforcement on the flow | Token confidentiality/integrity in transit | Deployer (§10) | + +## §6 Assumptions about inputs + +| Entry point | Parameter | Attacker-controllable? | Caller/deployer must enforce | +| --- | --- | --- | --- | +| RP plugin | `wresult` / SAMLResponse (the token) | **yes** (browser-delivered) | full signature + issuer + audience + conditions + replay + anti-XSW | +| RP/IdP | `wctx`, `wtrealm`, `wreply`, `wfresh`, `wreq`, `RelayState` | **yes** | validate realm; allow-list reply URLs; treat context as opaque | +| IdP login | username/password / external auth | **yes** | credential verification; CSRF; throttling | +| IdP metadata endpoint | request | **yes** | safe publication; no SSRF via metadata refs | +| RP/IdP config (truststore, realm, certs) | all keys | **no — deployer-trusted** | never sourced from a request | + +## §7 Adversary model + +- **Primary adversary:** an untrusted web user/browser who crafts, tampers with, or **replays** SAML tokens and + WS-Fed parameters to an RP, or attacks the IdP login/token endpoints. Capabilities: submit arbitrary + `wresult`/parameters, replay a previously valid token, attempt signature-wrapping, forge an assertion, + redirect via a manipulated `wreply`. +- **Secondary:** a malicious or compromised RP requesting tokens it should not receive; a network MITM where + TLS is absent. +- **Goals:** authenticate as another user / bypass authentication at the RP; have a token minted or forwarded + to an attacker-controlled RP; escalate roles by injecting `AttributeStatement` claims; open-redirect a user. +- **Out of model:** the deployer/operator; anyone holding the IdP signing key or the configured truststore; + the credential store internals. + +## §8 Security properties the project provides + +*(All *(inferred)* pending §14; symptom + severity per the rubric.)* + +1. **RP token authenticity & integrity.** The RP accepts a token only if its XML signature verifies against a + configured/trusted IdP certificate and the signature covers the asserted content (anti-XSW) + *(inferred — load-bearing)*. *Symptom:* acceptance of an unsigned/forged/wrapped token ⇒ authentication + bypass / impersonation. *Severity:* critical. +2. **Audience & freshness binding.** A token is accepted only if its audience/`wtrealm` matches this RP and its + `Conditions` (NotBefore/NotOnOrAfter, within skew) hold; consumed tokens are replay-protected + *(inferred)*. *Symptom:* cross-RP replay or replay of an expired/old token. *Severity:* critical. +3. **IdP token-issuance integrity.** The IdP issues tokens only for authenticated users and signs them; it + binds the token to the requesting realm and validates the reply target *(inferred)*. *Symptom:* token minted + without auth, or for/forwarded to the wrong RP. *Severity:* critical. +4. **Role/claim integrity.** Roles/claims come from the signed assertion, so a client cannot inject or elevate + claims *(inferred)*. *Symptom:* privilege escalation via tampered `AttributeStatement`. *Severity:* critical. +5. **Safe token parsing.** Malformed SAML/XML yields a clean rejection, not XXE/DoS/crash (inherits CXF/WSS4J + secure-XML defaults) *(inferred)*. *Symptom:* XXE/SSRF/OOM from a crafted token. *Severity:* critical. + +## §9 Security properties the project does NOT provide + +- **No security without correct trust configuration.** If the RP is not configured with the right IdP signing + trust, or audience validation is disabled, token validation is only as strong as what's left *(inferred)*. +- **No defence against a compromised IdP signing key or a deployer who trusts a hostile IdP** (§3). +- **No transport security by itself** — without TLS the token is interceptable/replayable on the wire. +- **The protected app must still authorize** beyond "authenticated"; Fediz establishes identity/claims, not + the app's access-control policy. + +**False friends:** + +- *"The token is signed" is not "the token is valid for me now"* — signature alone, without audience + + conditions + replay + anti-XSW checks, is the classic SAML-SSO bypass. +- *`wctx`/`RelayState` look like trusted state but are attacker-controlled* — treat as opaque, validate on return. +- *A published metadata document looks authoritative but is fetched over the same untrusted channels* — trust + is anchored in configured certs, not in fetched metadata. + +**Well-known attack classes to keep in view:** XML signature wrapping (XSW); unsigned-assertion / signature +-stripping acceptance; assertion replay and cross-audience replay; `Conditions`/clock-skew abuse; open redirect +via `wreply`; XXE/XML-DoS on token parse; CSRF on IdP login; recipient/audience confusion. + +## §10 Downstream (deployer) responsibilities + +- Configure the RP with the **correct IdP signing certificate(s)** and require signed tokens; **set the + audience/`wtrealm`** so tokens for other RPs are rejected. +- Enforce **TLS** on every leg of the flow; keep IdP↔STS and truststore material protected. +- **Allow-list reply URLs** at the IdP (`wreply`) to prevent open redirect / token forwarding. +- Keep the **replay cache** and a sane **clock-skew window** enabled; sync clocks. +- Protect IdP login from CSRF/brute-force; secure the credential store. +- Stay on a supported line and watch . + +## §11 Known misuse patterns + +- Deploying an RP without configuring/validating the IdP signing trust (accepts any token). +- Disabling audience or conditions validation "to make it work". +- Running the flow over plain HTTP (token sniffable/replayable). +- Leaving `wreply` reply URLs unrestricted at the IdP (open redirect / token forwarding). +- Treating `wctx`/`RelayState` as trusted server state. + +## §11a Known non-findings (recurring false positives) + +*(v0 seed — the PMC will have the authoritative list — §14.)* + +- **"Accepts a token" reports that assume validation is off** when the default requires signature + audience + + conditions + replay — `KNOWN-NON-FINDING` / misconfig unless a *default* check is missing (then `VALID`). +- **Findings in `examples/` / `systests/` / sample configs** — out of scope (§3). +- **"No TLS"** against sample/local configs — deployer responsibility (§10). +- **Use of SAML 1.1** (legacy but supported) flagged generically — supported by design; only specific weak + handling is a finding. + +## §12 Conditions that would change this model + +- A change to default signature/audience/conditions/replay enforcement in the RP. +- A new protocol (e.g. OIDC) or token type enabled by default. +- A change to `wreply` reply-URL validation or IdP login defaults. +- A change in the inherited CXF/WSS4J XML-security defaults. +- Any report not cleanly routable to a §13 disposition. + +## §13 Triage dispositions + +| Disposition | Meaning | Licensed by | +| --- | --- | --- | +| `VALID` | Violates a claimed property via an in-scope adversary/input *in a default/secure configuration*. | §8, §6, §7 | +| `VALID-HARDENING` | No §8 property broken, but a §11 misuse is easy enough to warrant a safer default/guard. | §11 | +| `OUT-OF-MODEL: trusted-input` | Requires control of deployer config / signing keys / truststore. | §6 | +| `OUT-OF-MODEL: adversary-not-in-scope` | Requires deployer / IdP-key / credential-store capability. | §7, §3 | +| `OUT-OF-MODEL: unsupported-component` | Lands in examples, systests, packaging, samples. | §3 | +| `OUT-OF-MODEL: non-default-build` | Only when the deployer disabled a validation control. | §5a | +| `BY-DESIGN: property-disclaimed` | Concerns a §9-disclaimed property (no security without trust config; identity ≠ app authorization). | §9 | +| `KNOWN-NON-FINDING` | Matches a §11a entry. | §11a | +| `MODEL-GAP` | Routes to none of the above → revise the model. | §12 | + +## §14 Open questions for the maintainers + +**Wave 1 — RP validation defaults (the SSO crux; §5a/§8):** +1. By default, does the RP **require a valid XML signature from a configured/trusted IdP cert** and reject + unsigned/untrusted tokens? *Proposed:* yes, mandatory. +2. Are **audience/`wtrealm`** and **`Conditions`** (NotBefore/NotOnOrAfter + skew) enforced by default, and is + a **replay cache** on by default? *Proposed:* all on by default. +3. Does the SAML processing defend **signature-wrapping (XSW)** by binding the verified signature to the + consumed assertion/elements? *Proposed:* yes (via WSS4J/CXF). + +**Wave 2 — IdP & redirect surface (§4/§8/§9):** +4. Does the IdP **validate `wreply` reply URLs** (allow-list) to prevent open redirect / token forwarding, and + is its login **CSRF-protected and throttled**? *Proposed:* reply-URL allow-list configured per realm; login + CSRF-protected. +5. How is the underlying **XML/SAML parsing hardened** against XXE/DoS — inherited from CXF/WSS4J secure + defaults? *Proposed:* yes, inherited. + +**Wave 3 — boundaries & §11a (§3/§9/§11a):** +6. Confirm the **deployer-misconfiguration** boundary (which "insecure" outcomes are `OUT-OF-MODEL`). *Proposed:* + per §3/§11. +7. From the advisory history, what do scanners most often (re)report that the PMC considers a **non-finding**? + (Seeds §11a.) + +**Meta:** +8. Confirm this model lives in `apache/cxf-fediz` (linked from its `SECURITY.md`), separate from the + `apache/cxf` umbrella. *Proposed:* yes. + +## §15 Machine-readable companion + +Deferred for v0; a `threat-model.yaml` can later encode the §6 trust table, §2/§3 scoping, §8 rows, §9 false +friends, §11a non-findings, and §13 dispositions.