auditing-saml-and-oidc-federation-trust · git:20260829.b60f9b2 · 2026-08-29 · sha256 83ed94736b6e326e

auditing-saml-and-oidc-federation-trust git:20260829.b60f9b2A

Immutable. This exact content is served forever at /api/v1/blob/83ed94736b6e326e.

---
name: auditing-saml-and-oidc-federation-trust
description: >-
  Audit federated single sign-on for assertions a relying party should not trust: a SAML response whose
  signature is not verified over the right element so a wrapped or altered assertion passes, an OIDC ID token
  whose issuer, audience, or nonce is unchecked, a relying party that accepts an assertion for any user
  because the subject or email is trusted without binding, and a federation that honors an identity provider or
  signing key it should not. Covers SAML and OpenID Connect where a relying party consumes assertions or ID
  tokens minted by an identity provider to authenticate users. Use when login trust crosses from an identity
  provider to a relying party and assertion validation is the boundary. The forged or misbound assertion is
  the source, the authenticated session it grants is the sink, and the missing signature, issuer, audience, or
  binding check that accepts it is the bug.
license: MIT
---

# Auditing SAML and OIDC federation trust: the assertion is only as good as its verification

Federated login moves the authentication decision to an identity provider and hands the relying party a signed
assertion, a SAML response or an OIDC ID token, that says who the user is. The relying party's entire security
then rests on verifying that assertion correctly, and the ways to verify it incorrectly are well worn. SAML
signature wrapping slips an unsigned, attacker-authored assertion alongside a signed one so the application
reads the wrong element; a signature checked over the wrong scope, or not at all, accepts a tampered response.
An OIDC ID token whose issuer, audience, or nonce goes unchecked can come from the wrong provider, be minted
for a different application, or be replayed. And a relying party that trusts a subject or email in the
assertion without binding it to the verified issuer will authenticate any user an attacker names. The audit is
not whether federation is configured but whether a forged or misbound assertion is refused. You audit this by
testing each validation the relying party must perform on the assertion.

## When to use

- A relying party consumes SAML assertions or OIDC ID tokens from an identity provider to authenticate users.
- Signature, issuer, audience, or nonce validation on the assertion may be missing or scoped wrong.
- The relying party may trust a subject, email, or attribute without binding it to the verified issuer.

## Scope check

Test federation trust only against relying parties and identity providers you own or are authorized to assess,
on non-production accounts. Forging or replaying assertions attempts real authentication, so use non-production
identities and never authenticate as a real user. If you can't name the authorization, stop.

## The loop

1. **Establish the intended trust first.** Name which identity provider and signing key the relying party
   should trust, the expected issuer and audience, and how the assertion's subject maps to a local user. This
   is the false-positive killer: a relying party that verifies the signature over the correct element against
   the expected key, checks issuer, audience, and nonce, and binds the subject to the verified issuer is
   correct. Name the intended trust, then test each check.

2. **Check SAML signature verification and wrapping.** For SAML, confirm the signature is verified over the
   exact element whose contents are consumed, and that the application reads the signed assertion, not an
   unsigned one an attacker added. Test signature wrapping: a response containing a valid signed assertion plus
   an injected unsigned assertion the application might read instead. Confirm a tampered or wrapped response is
   rejected.

3. **Check OIDC issuer, audience, and nonce.** For OIDC, confirm the ID token's issuer matches the expected
   provider, its audience matches this relying party, its signature verifies against the provider's current
   key, and the nonce ties it to this login request. A token from the wrong issuer, minted for a different
   audience, or replayed without a nonce check authenticates something the relying party did not request.

4. **Check subject binding and attribute trust.** Confirm the relying party maps the user from the assertion's
   subject as asserted by the verified issuer, not from an email or attribute it trusts without binding. Test
   whether an assertion naming a different user's email or identifier authenticates as that user. An identity
   taken from an unbound attribute lets an attacker assert any user.

5. **Check the trusted providers and keys.** Confirm the relying party honors only the specific identity
   provider and signing keys it should, rejects assertions from unknown issuers or keys, and rotates and pins
   keys correctly. A relying party that trusts any provider in a shared federation, or accepts a key it should
   not, can be given a validly signed assertion from the wrong source.

6. **Confirm and record.** Confirm by getting a forged, wrapped, wrong-issuer, wrong-audience, replayed, or
   misbound assertion accepted as a login, on non-production accounts and without authenticating as a real
   user. Kill the lead if the relying party verifies the signature over the right element against the expected
   key, checks issuer, audience, and nonce, binds the subject to the verified issuer, and trusts only the
   intended providers and keys. Record the forged or misbound assertion, the authenticated-session sink, and
   the missing signature, issuer, audience, or binding check.

## Where federation trust leaks

- **Signature wrapping reads the wrong element.** An unsigned assertion injected beside a signed one is
  consumed if the application does not verify the signature over exactly what it reads.
- **An unchecked issuer or audience accepts the wrong token.** An OIDC ID token from the wrong provider or
  minted for a different application passes when issuer and audience are not verified.
- **A missing nonce enables replay.** An ID token not bound to the login request by a nonce can be replayed
  into a session.
- **Unbound subject or email asserts any user.** Taking the identity from an attribute the relying party trusts
  without binding to the verified issuer lets an attacker name any user.
- **An over-broad federation trusts the wrong source.** Honoring any provider or key in a shared federation
  accepts a validly signed assertion from a source it did not intend.

## Worked example (a confirm and a kill)

> **Confirm.** A SAML relying party verifies that the response contains a valid signature but reads the subject
> from an assertion element without confirming the signature covers that element. A crafted response carries a
> legitimately signed assertion plus an injected unsigned assertion naming a different user, and the application
> reads the injected one, authenticating as the attacker-named user. **Confirmed** SAML signature-wrapping
> authentication bypass, `critical`, remediation = verify the signature over exactly the element whose contents
> are consumed, reject responses with multiple or unsigned assertions, and bind the authenticated user to the
> signed subject only.
>
> **Kill.** The relying party verifies the signature over the exact consumed element against the expected
> provider key, rejects responses containing unsigned or extra assertions, checks issuer and audience (and nonce
> for OIDC), maps the user from the signed subject bound to the verified issuer, and trusts only the one
> configured provider and its pinned keys. A wrapped, wrong-issuer, or misbound assertion is refused. **Killed**,
> `kill_reason` = "signature verified over the consumed element, issuer/audience/nonce checked, subject bound to
> the verified issuer, and only the intended provider trusted; no forged or misbound assertion authenticates."

## Rationalizations to reject

- *"The response is signed."* → Confirm the signature covers exactly the element you read and there is no extra
  unsigned assertion; a signed response can still carry a wrapped forgery.
- *"The token came from our provider."* → Verify the issuer and audience on the token itself; without them a
  token from the wrong provider or for a different app passes.
- *"We match the user by email."* → An email or attribute not bound to the verified issuer lets an attacker
  assert any user; map the user from the signed subject.
- *"Nonce is optional."* → Without a nonce an ID token can be replayed into a session; bind the token to the
  login request.
- *"We trust the federation."* → Confirm you trust only the specific provider and keys intended; a broad
  federation accepts a valid assertion from a source you did not mean to honor.

## Executing this in practice

You need the expected provider, issuer, audience, and signing keys, exactly what the relying party verifies on
the assertion and over which element, how the subject maps to a local user, and the trusted-provider set. For
each, test a wrapped or tampered SAML response, a wrong-issuer or wrong-audience or replayed OIDC token, and a
misbound subject. Reading the assertion-validation code shows the intended trust; a forged assertion accepted
as login shows whether it holds.

## Related

- `auditing-oauth-token-audience-and-scope-trust` - the access-token companion; audience and issuer confusion
  are the same failure at the authorization-token layer.
- `auditing-jwt-verification-and-key-trust` - OIDC ID tokens are JWTs, so the algorithm and key-trust checks
  there are prerequisites for the issuer and audience checks here.
- `auditing-session-lifecycle-and-fixation` - once federation grants a session, its lifecycle and fixation
  properties are the next boundary this pairs with.
- `auditing-account-recovery-and-reset-trust` - federated identity and account recovery are two routes to the
  same account; audit both so one does not undercut the other.
- [FINDING-SCHEMA.md](../../FINDING-SCHEMA.md) - source = the forged or misbound assertion, sink = the
  authenticated session it grants, evidence = the missing signature, issuer, audience, or binding check.