Identity & Access · Chapter 10 of 16
SAML and enterprise federation
XML, assertions, and three signature attacks that broke real products. Still the backbone of enterprise SSO, and worth understanding precisely rather than avoiding.
SAML is older than OAuth, is built on XML, and is not going away — because it is embedded in every enterprise application procured in the last twenty years. If you work in identity you will meet it, and the parts worth knowing are the assertion structure, the trust model, and why its security failures were structural rather than incidental.
The two parties and the trust model
- Identity Provider (IdP) — authenticates the user and issues a signed assertion. Entra, Okta, Ping, ADFS, Keycloak.
- Service Provider (SP) — the application. It trusts assertions signed by a specific certificate belonging to a specific IdP.
- Trust is established out of band, by exchanging metadata: entity IDs, endpoints and the signing certificate. There is no discovery and no dynamic registration. This is why SAML integrations involve a spreadsheet and a support ticket, and why certificate rotation is an event rather than a routine.
SP-initiated versus IdP-initiated, and why one is worse
SP-initiated: the user hits the application, which redirects to the IdP with an AuthnRequest, and the IdP posts an assertion back to the application's Assertion Consumer Service URL. This is the default and the safer one, because the SP started the flow and can correlate the response to its own request via the InResponseTo field.
IdP-initiated: the user clicks a tile in a portal and the IdP posts an unsolicited assertion. Convenient, widely deployed, and structurally weaker — there is no request to correlate against, so the SP cannot distinguish an assertion it expected from one an attacker replayed. If you can avoid IdP-initiated, do. If you cannot, the compensating controls are strict single-use enforcement on assertion IDs, a very short NotOnOrAfter, and rejecting any assertion whose Recipient is not your exact ACS URL.
What to validate on every assertion
- The signature, against the pinned IdP certificate — not against whatever certificate the document happens to embed. ‘The signature is valid’ and ‘the signature is from the party I trust’ are different statements, and conflating them has been a real vulnerability in multiple products.
- That the assertion itself is signed, not merely the response wrapping it. Signing the response alone leaves the assertion swappable.
AudienceRestrictionnames your entity ID. Without this, an assertion issued for a different application is accepted by yours.Recipientmatches your ACS URL exactly.NotBeforeandNotOnOrAfter, with tight skew.- The assertion ID has not been seen before — replay protection requires state, and this is the check implementations most often skip because it is the only one that requires a store.
InResponseTomatches a request you actually made, for SP-initiated flows.
The three attack classes, and the lesson underneath them
XML Signature Wrapping. XML signatures cover a referenced subtree, not the whole document. An attacker moves the legitimately signed assertion into a location the signature verifier still finds, and inserts an unsigned forged assertion where the application's business logic reads. Signature verification passes on the real element; the application acts on the fake one. This broke multiple major SSO implementations.
Comment truncation (the ‘golden SAML’ family of parser bugs). An attacker with a legitimate account inserts an XML comment into the NameID: admin<!--x-->@corp.com. Canonicalisation for signing sees the full string, but many XML libraries return only the text node before the comment when reading the value — so the signature is valid over one string and the application reads another. A vulnerability in a text-node API became an authentication bypass across many products simultaneously.
Unverified signer. Accepting the certificate embedded in the assertion rather than the one you configured. The signature verifies perfectly, against the attacker's own key.
The lesson. All three come from the same structural problem: cryptographically signing a subset of a mutable, canonicalisable document, then parsing that document with a general-purpose library. Verify first, then extract, and extract from the same element that was verified — never from a re-parse. And do not write your own; use a maintained library that has already been through these.
SAML versus OIDC, answered as a design decision
| Dimension | SAML | OIDC |
|---|---|---|
| Encoding | XML with XML-DSig | JSON with JWT |
| Attack surface | Canonicalisation, wrapping, parser inconsistency | Algorithm confusion, missing claim validation |
| Client types | Browser-based web applications | Web, mobile, single-page, native, machine |
| Trust setup | Metadata exchange, out of band | Discovery document, dynamic registration possible |
| Session semantics | Rich — SLO, session index, authentication context | Simpler; back-channel logout is a later addition and unevenly supported |
| Where it lives | Enterprise SaaS procured over two decades | Anything built recently, and everything mobile |
| Choose it when | The application only supports SAML — which is the usual reason | You have a choice |
The mature position for an interview: “New integrations get OIDC. SAML stays where the application requires it, which is a large installed base and will be for years. The migration is driven by the application vendor, not by us, so identity teams run both indefinitely and the operational cost is real — two sets of certificates, two rotation processes, two sets of logout semantics.”
The certificate rotation that takes companies down
The IdP's signing certificate expires, typically after one to three years. When it does, every SP that pinned it rejects every assertion, and everyone loses access to everything at the same moment. It is the identity equivalent of the TLS expiry outage and it is more disruptive, because SSO is the front door to everything.
The controls are simple and frequently absent: an inventory of every SP and its configured certificate; alerting well before expiry; and where the SP supports it, publishing both the old and new certificates in metadata during an overlap window so the rotation is not a flag-day. Being the person who asks “when does our IdP signing certificate expire, and who holds the list of relying parties?” is a very cheap way to be useful.
Check yourself