
Validating a SAML Assertion Correctly
A technical walkthrough on validating SAML assertions, covering audience restrictions, timestamp checks, and replay prevention for secure identity flows.
This is part 6 of the SAML 2.0 for Engineers series.
Receiving a SAML assertion is not the finish line; it is the starting gate for a critical security protocol. A common misconception among developers is that a valid XML Digital Signature (XMLDSig) is sufficient to trust the identity provider (IdP) and log the user in. This is a dangerous oversimplification.
Validating a SAML assertion is a multi-layered enforcement mechanism. A signature only proves that the data originated from the claimed IdP and hasn’t been altered in transit. It does not prove that the assertion is valid for your specific Service Provider (SP), that it is still within its valid time window, or that it hasn’t been used before. If you skip audience checks, time validation, or replay prevention, you expose your system to token injection, indefinite session reuse, and impersonation attacks.
This walkthrough details the four mandatory validation steps every Service Provider must implement to secure identity flows.
Cryptographic Signature Verification
During assertion validation, the first step is to verify the XML Digital Signature. This cryptography-based check ensures integrity and authenticity, confirming that the assertion has not been tampered with since the identity provider signed it. This verification is critical for secure SSO flows.
The mechanism involves three sub-steps:
- Locate the Signature: Find the
<ds:Signature>element within the<Assertion>block. - Fetch the Public Key: Do not trust the key embedded in the assertion if it’s a self-signed certificate from an untrusted source. Instead, fetch the IdP’s metadata (usually via
https://idp.example.com/metadata.xml) and locate theKeyDescriptorforsigning. - Verify: Use a standard XMLDSig library (e.g.,
xmlsec,saml2-js) to verify the signature against the public key. As defined in RFC 3275 and the XML Signature Syntax and Processing, proper canonicalization and digest algorithms must be applied.
If this verification fails, discard the assertion immediately. Do not log the error as a simple "login failed"; log it as a security event for further investigation.
Opinion: Never implement your own XML signature verification. The canonicalization rules (C14N) are subtle and easy to get wrong, leading to signature wrapping attacks. Use a mature, audited library.
Audience Restriction Enforcement
SAML assertions are often designed to be reusable across multiple Service Providers. To prevent token leakage, the <Conditions> block contains an <AudienceRestriction> element that explicitly states who this token is intended for.
<Conditions>
<AudienceRestriction>
<Audience>https://app.mycompany.com/saml/metadata</Audience>
</AudienceRestriction>
</Conditions>The Mechanism:
Your SP must extract its own EntityID (e.g., https://app.mycompany.com/saml/metadata) and compare it against every <Audience> URI in the assertion. As specified in the SAML 2.0 Core Specification, this restriction ensures that the assertion is only processed if the recipient matches the intended audience.
If your SP’s EntityID is not present in the list, the assertion is invalid for your context. An attacker might intercept a valid token intended for a partner service and replay it against your login endpoint. Without this check, you would accept the token and grant access, even though the IdP never authorized your app to receive this identity.
Temporal Validation: NotBefore and NotOnOrAfter
SAML assertions have a finite lifespan. The <Conditions> block also contains NotBefore and NotOnOrAfter timestamps. These define the window during which the assertion is valid.
<Conditions NotBefore="2023-10-27T10:00:00Z" NotOnOrAfter="2023-10-27T10:05:00Z">
<!-- ... -->
</Conditions>The Clock Skew Problem: Server clocks are rarely perfectly synchronized. If the IdP signs an assertion at 10:00:00 UTC, but your server’s clock is 30 seconds fast, you might reject the assertion because it appears "not yet valid." Conversely, if your clock is slow, you might accept an expired token.
Implementation Strategy:
- Strict Check: Verify that
current_time >= NotBeforeandcurrent_time <= NotOnOrAfter. - Tolerance Window: Apply a configurable tolerance (typically ±5 minutes) to account for NTP drift.
- Effective
NotBefore=NotBefore- 5 minutes - Effective
NotOnOrAfter=NotOnOrAfter+ 5 minutes
- Effective
Warning: Do not ignore the
notonoraftertimestamp. Some IdPs use short-lived assertions (e.g., 2 minutes) for high-security flows. Ignoring this allows indefinite session reuse if combined with a weak replay prevention strategy.
Replay Attack Prevention
SAML assertions are stateless. Once an assertion is valid, it remains valid until it expires or the IdP revokes it (which is rare). If an attacker captures a valid assertion (via network sniffing, browser history, or logs), they can reuse it to impersonate the user indefinitely.
Effective saml security requires preventing replay attacks by ensuring each token is consumed only once.
The Mechanism:
Every SAML assertion has a unique ID attribute (e.g., ID="_abc123..."). This ID is generated by the IdP and is globally unique for that specific assertion instance.
Your SP must implement a replay cache:
- Extract the ID: Parse the
IDattribute from the<Assertion>root element. - Check Cache: Query your cache (Redis, Memcached, or in-memory LRU) for this
ID. - Reject if Found: If the
IDexists, reject the request. The assertion has already been consumed. - Store if New: If the
IDis not found, store it in the cache. - TTL Alignment: Set the cache TTL to match the assertion’s
NotOnOrAftertime. This ensures the cache entry expires automatically when the assertion becomes invalid.
Why This Matters: Without replay prevention, a stolen token can be used to log in repeatedly. With it, each token is single-use. This is critical for protecting against man-in-the-middle attacks and credential theft.
Summary Checklist
When validating a SAML assertion, ensure your code executes these checks in order:
- Signature: Verify against IdP metadata public key.
- Audience: Confirm your SP’s EntityID is in
<AudienceRestriction>. - Time: Check
NotBeforeandNotOnOrAfterwith clock skew tolerance. - Replay: Ensure the assertion
IDhas not been seen before in your cache.
Skipping any of these steps introduces a vulnerability. Signature verification protects integrity. Audience restriction protects context. Time validation protects freshness. Replay prevention protects uniqueness. Together, they form a strong defense for your identity flow.
Related posts
SAML Assertions, Statements, and the Schema
An examination of SAML 2.0 assertion structure, statement types, and schema validation for developers and identity engineers.
SAML Single Logout (SLO): The Mechanics of Session Termination
A technical examination of SAML Single Logout (SLO) mechanisms, covering front-channel and back-channel flows, session index handling, and protocol compliance for identity engineers.
SAML 2.0 in One Diagram
A visual walkthrough of the SAML 2.0 Single Sign-On flow, covering bindings, profiles, and how it compares to OIDC.