Back to Blog
STANDARDS

SAML 2.0 for Developers: Implementation Pitfalls and Security Gotchas

16 min readEmre Sakarya
SAML::XSW

SAML 2.0 has a reputation among developers as "the protocol you set up once and never touch again." That reputation is partly true and partly dangerous. The set-it-once part is real — once a SAML integration is wired between an identity provider and a service provider, it usually runs without operational attention for years. The dangerous part is that the bugs that ship in a SAML integration almost always ship at setup time and almost never get caught by routine application security tooling, because SAML's attack surface is in protocol semantics rather than code patterns. This post walks the SAML-specific implementation pitfalls that produce real-world incidents — XML signature wrapping, assertion replay, metadata poisoning, signature stripping, and the identity provider setup mistakes that account for most of the bugs deployed in workforce SSO integrations today. For the broader comparison of SAML against OAuth and OIDC, see the OIDC vs SAML vs OAuth 2.0 developer comparison; this post assumes you have made the SAML decision and want to ship it correctly.

Why SAML Pitfalls Are Hard to Catch

The first thing to understand about SAML bugs is why standard application security tooling misses most of them. SAST tools find code-pattern bugs — unparameterized SQL queries, unescaped output, JWT signature verification skipped. SAML bugs are different in kind. The XML parser is correct; the signature verification call is correct; the protocol library is current. The bug is that the application reads identity attributes from the wrong XML element, accepts an assertion intended for a different audience, processes metadata from an untrusted source, or accepts the absence of a signature where it should require one. These are not code-pattern bugs; they are policy and protocol-semantic bugs that look correct in isolation and are wrong only in context.

The second thing to understand is that SAML's installed base is conservative. A SAML integration written in 2015 against a library released in 2014 is often still running in production in 2026, against an identity provider that has hardened its side but cannot retroactively fix the service provider's processing logic. The pitfalls below are not theoretical — they have shipped in major enterprise SSO integrations within the last several years, and the same patterns continue to ship in new integrations because the documentation that walks developers through SAML setup typically optimizes for working integration rather than secure integration.

XML Signature Wrapping (XSW) — The Canonical SAML Bug

XML Signature Wrapping is the SAML attack pattern that has accounted for the most published vulnerabilities and the most CVEs against major identity providers and SAML libraries. The mechanism: a SAML assertion is an XML document containing a signed element (the assertion itself) and an unsigned envelope (the SAML Response wrapping it). An attacker who captures a signed assertion can manipulate the XML structure around it — adding wrapping elements, moving the signed element to a different position in the document, duplicating element IDs — such that the signature still verifies against the original signed element while the application's attribute-extraction logic reads attributes from a different (attacker-controlled) element.

The attack's effectiveness depends on a specific implementation mistake at the service provider: signature verification and attribute extraction operate on different elements of the XML document. A naive implementation might verify the signature on the SAML Response's child Assertion element, then re-query the document with an XPath expression like //AttributeStatement/Attribute[@Name='email'] to extract user identity. The XPath query operates on the entire document; it will return any matching element, including a wrapper element the attacker injected, which is processed for attributes while the original signed Assertion sits unchanged and signature-valid in a different part of the tree.

The defense against XSW is structural, not detection-based. A SAML library that correctly defends against XSW maintains the link between signature verification and attribute extraction — when the signature is verified on an element, all subsequent attribute reads must operate on that element specifically, not on the document at large. The library returns a reference to the verified element and refuses to expose attributes through document-level queries. Applications using a current SAML library typically inherit this protection without needing to implement it explicitly; applications using hand-rolled SAML processing very often do not.

The audit pattern for catching XSW exposure in an existing SAML integration is to look for code that verifies a signature in one step and reads attributes in a separate, later step that doesn't reference the verified element. If the attribute-extraction code uses XPath, DOM traversal, or any other document-level query rather than operating on a returned reference to the verified element, the integration is likely vulnerable. The fix is either to migrate to a SAML library that enforces the structural binding (current versions of most major libraries do) or to refactor the processing to operate exclusively on the signature-verified element.

SAML Assertion Replay

A signed SAML assertion is a bearer credential — anyone in possession of it can present it to the service provider, and the service provider will accept it if the assertion's conditions are satisfied. The conditions on a SAML assertion include a NotBefore timestamp (the assertion is not valid before this time) and a NotOnOrAfter timestamp (the assertion expires at this time), plus an optional AudienceRestriction (the assertion is only valid for the named audience). Without strict validation of these conditions, a captured assertion can be replayed against the service provider for the duration of its validity window — and against any service provider that doesn't validate the audience restriction it should have.

The replay attack vectors are several. A man-in-the-middle on an unencrypted connection between the identity provider and the service provider can capture the assertion in transit; this is mitigated by TLS, which is the modern baseline. A compromised intermediate proxy, debugging logging that captures assertion XML, or HTTP referer leakage from the service provider's pages can also expose assertions. Browser-cached redirect responses containing assertions are a less-discussed exposure.

The defenses are layered. Short assertion validity windows (5 minutes is the typical recommendation; longer windows widen the replay opportunity). Strict service-provider-side validation of the AudienceRestriction (the audience must exactly match the service provider's expected audience identifier). One-time-use assertion tracking for high-security scenarios (the service provider maintains a short-term cache of recently-accepted assertion IDs and refuses to accept the same ID twice). Strict NotBefore and NotOnOrAfter timestamp validation with clock skew tolerance bounded at a few minutes maximum.

The implementation pitfall most often associated with assertion replay is generous clock skew tolerance. A service provider that allows 10 or 15 minutes of clock skew on either side of the validity window is effectively running with assertions valid for 25-35 minutes, which is well outside the safe replay window. The clock-skew tolerance should be the minimum necessary to accommodate realistic NTP drift — 60 seconds is generous; 300 seconds is the absolute outer bound.

Metadata Poisoning and Distribution

SAML trust is anchored in metadata documents. The identity provider publishes a metadata document describing its endpoints, its signing certificate, and its SAML configuration; the service provider publishes a parallel metadata document describing its endpoints, expected audience, and assertion consumer service URL. Each side loads the other's metadata to know which keys to trust and where to send messages. The integrity of the entire SAML trust relationship depends on the integrity of these metadata documents.

Metadata poisoning is the attack where a malicious metadata document is introduced into a service provider's trust store, replacing the legitimate identity provider's signing certificate with one the attacker controls. The attacker then issues malicious assertions signed with their key, and the service provider accepts them because the metadata trusts the attacker's key as the identity provider's. The attack vectors are typically not "intercept metadata in transit" — most metadata distribution uses HTTPS — but "social-engineer the service provider administrator into trusting a malicious metadata document" or "compromise the metadata distribution channel itself."

The defenses against metadata poisoning are operational. Metadata should be signed by a publisher key separate from the assertion-signing key, with the publisher key distributed through a trustworthy channel (often a federation operator that the identity provider and service provider both trust). Metadata signatures should be verified on every refresh. Metadata refresh should happen automatically on a regular schedule from a known, authoritative URL — not loaded once and trusted forever. Manual metadata loading should require strong authentication of the source.

For organizations participating in identity federations (Research and Education networks, Microsoft 365 commercial cloud, government federations), the federation operator typically handles metadata distribution and signing. For point-to-point integrations between two organizations, the metadata distribution is typically ad-hoc and bears watching — most metadata poisoning incidents in production have been point-to-point integrations rather than federation-mediated ones.

Signature Stripping and Algorithm Confusion

A signed SAML assertion has an XML Signature element in its document tree that the service provider's library locates and verifies. A surprisingly common vulnerability pattern is service providers that accept assertions without signatures — either because the configuration didn't require signatures to be present, or because the signature-verification library treated a missing signature as a no-op rather than a failure. The attack is to strip the signature from a captured assertion, modify the attributes, and present the unsigned assertion to a service provider that doesn't require signatures.

The defense is configuration-level — the service provider must require signatures and reject assertions that lack them. Most modern SAML libraries default to requiring signatures, but the default is occasionally overridden during integration testing and then never restored. The audit pattern is to verify in the production configuration that signature requirement is enforced, not just that the integration is signed in normal operation.

Algorithm confusion is a related but distinct attack pattern. SAML supports multiple signature algorithms (RSA-SHA256, RSA-SHA1, ECDSA, and historically several others). A service provider that allows the assertion to specify the algorithm — and then verifies the signature using that algorithm — is exposed if any of the supported algorithms have known weaknesses (RSA-SHA1 is now broken; SHA-1 collisions have been demonstrated since 2017). The defense is to enforce the signature algorithm at the configuration level: the service provider specifies which algorithm is acceptable, and assertions signed with any other algorithm are rejected regardless of what the assertion claims.

Identity Provider Setup — Google Workspace, Entra ID, Okta

The pitfalls above are at the protocol level. The setup-time pitfalls are at the configuration level, and they tend to cluster around a few specific identity provider integrations. The patterns worth knowing per platform:

Google Workspace SSO via SAML. Google Workspace acts as identity provider for SAML-integrated SaaS applications through the Admin Console's SAML app catalog or custom SAML app definitions. The google saml iam identity center setup configuration — the operational sequence developers most often look up when integrating Workspace SSO — involves loading the service provider's metadata (or entering the ACS URL and audience manually), assigning the SAML app to specific Google users or organizational units, and configuring attribute mappings between Google user attributes and SAML attribute statements. The most common setup mistake in the google saml iam identity center setup configuration flow is configuring the SAML app at the organization root and forgetting to scope it to the appropriate users — which produces an SSO relationship that gives every Google user in the organization access to the service provider, often inadvertently. The fix is to scope SAML app assignment to specific OUs or groups during initial setup, not after. Note that "IAM Identity Center" appears here as the integration pattern between Google Workspace as identity provider and AWS IAM Identity Center (formerly AWS SSO) as a common service provider — the same setup steps apply to other SAML-aware service providers; AWS IAM Identity Center is the most-cited integration point because it is one of the most common Workspace SSO targets in production deployments.

Microsoft Entra ID (formerly Azure AD). Entra ID's SAML configuration involves creating an Enterprise Application, configuring the SAML signing certificate, defining the SAML Sign-on URL and Identifier (audience), and assigning users or groups to the application. Entra ID supports both SP-initiated and IdP-initiated SAML flows; the IdP-initiated flow is more commonly misconfigured. The pitfall pattern is to enable IdP-initiated SSO without configuring RelayState validation at the service provider, which leaves the service provider vulnerable to a class of CSRF-like attacks where an attacker can deliver a valid SAML assertion to a victim's session.

Okta SAML. Okta's SAML integration is similar to Entra ID's in shape but historically has had more granular control over assertion conditions, attribute statements, and signature options. The pitfall most often associated with Okta SAML integrations is misconfigured AudienceRestriction — Okta lets you specify multiple audiences, and developers occasionally specify a wildcard or omit the audience entirely, leaving the assertion accepted by any service provider that doesn't independently validate audience. The fix is to specify exactly one audience matching the service provider's expected audience identifier.

Multi-tenant SAML. For applications acting as service provider for multiple customer identity providers (the SaaS SSO scenario), the setup pattern is per-customer SAML configurations isolated from each other. The pitfall is shared configuration — applications that load a single SAML metadata document for all customers, or that use a single audience identifier across all customers, create cross-customer assertion-validity confusion. The fix is per-customer audience identifiers, per-customer trust anchors, and per-customer assertion validation.

Implementation Patterns That Work

The implementation pattern that produces the fewest SAML bugs has six properties. First, use a maintained SAML library appropriate to the application's stack — the Python SAML libraries (python3-saml, pysaml2), the Java SAML libraries (Spring Security SAML, OpenSAML), the Node.js libraries (passport-saml, samlify), and the equivalent libraries for other stacks. Roll-your-own SAML is the source of most published SAML CVEs against enterprise applications. Second, configure the library to require signatures on all incoming assertions and to enforce the signature algorithm at the configuration level rather than reading it from the assertion. Third, enforce strict AudienceRestriction validation with exactly one allowed audience matching the service provider's expected identifier.

Fourth, set NotBefore and NotOnOrAfter validity windows to 5 minutes with clock skew tolerance bounded at 60 seconds. Fifth, configure metadata loading from a known authoritative URL with metadata signature verification and scheduled refresh — never load metadata once and trust it indefinitely. Sixth, log every assertion processing event with sufficient detail (issuer, subject, conditions, signature status) to support forensic analysis after an incident; SAML processing without audit logs leaves no trail when something goes wrong.

The verification pattern that complements the implementation pattern is to periodically exercise the SAML integration with deliberate test cases — assertions with expired conditions, assertions with mismatched audiences, assertions with stripped signatures, assertions with altered attributes. A SAML integration that accepts any of these test cases has a misconfiguration; one that rejects all of them is functioning as intended. The test cases can be generated with the SAML Raider tool (a Burp Suite extension) or with hand-crafted assertion templates; the discipline matters more than the specific tool.

For broader application security verification, SAML implementation falls under ASVS V6 (Authentication) and V7 (Session Management) — the ASVS 5.0 developer guide covers the chapter-level requirements. For the broader federated identity comparison framing this post sits inside, see the OIDC vs SAML vs OAuth 2.0 developer comparison.

SAML implementation: questions developers ask

How do I set up Google SAML with IAM Identity Center?

In Google Workspace Admin Console, create a SAML app from the catalog or define a custom SAML app. Load the service provider's metadata (or enter ACS URL and audience manually), configure attribute mappings between Google user attributes and SAML attribute statements, and scope the SAML app to specific OUs or groups — not to the organization root. The most common configuration mistake is leaving the app assigned to all users by default; scope it to the intended user population during initial setup.

What is XML Signature Wrapping in SAML?

XML Signature Wrapping (XSW) wraps a signed SAML assertion in additional XML structure such that the signature verifies against the original signed element while the application's attribute-extraction logic reads attributes from a substituted, attacker-controlled element. The defense is structural: attribute extraction must operate on the same element the signature verified, not on the document at large. Modern SAML libraries enforce this binding; hand-rolled SAML processing typically does not.

How long should a SAML assertion be valid?

Five minutes is the typical recommendation. Longer validity windows widen the replay opportunity. Clock skew tolerance should be bounded at 60 seconds — generous enough to accommodate realistic NTP drift, tight enough not to widen the effective validity window. The full effective window (validity + 2x clock skew) should be at most 7-8 minutes for normal applications, shorter for high-security ones.

Should I require signatures on SAML assertions?

Yes, always. The service provider must require signatures and reject assertions that lack them. Most modern SAML libraries default to requiring signatures, but the default is occasionally overridden during integration testing and then never restored. Verify in the production configuration that signature requirement is enforced — not just that the integration happens to be signed in normal operation.

How do I prevent metadata poisoning?

Load metadata from a known authoritative URL over HTTPS, verify metadata signatures using a publisher key distributed through a trustworthy channel, and refresh metadata on a scheduled basis rather than loading once and trusting indefinitely. For point-to-point integrations between two organizations, treat metadata distribution as a security-sensitive step — most metadata poisoning incidents have been in point-to-point integrations rather than federation-mediated ones.

Can I use SAML and OIDC together?

Yes, this is a common architecture for organizations with mixed workforce SSO and modern application requirements. The pattern is to run SAML at the edge — a SAML-aware identity gateway converts the SAML assertion from the corporate identity provider into internal OIDC ID tokens — and use OIDC throughout the application stack behind that gateway. The application sees a clean OIDC interface; SAML stays at the corporate-integration boundary where it works well.