Skip to content
Ashish.
All posts
Diagram illustrating the four layers of SAML assertion validation.
6 min readSecurityDevelopers, Security EngineersFeatured#saml#security#authentication#sso#identity#validation#xml#cryptography

Validating a SAML Assertion Correctly

A technical walkthrough on validating SAML assertions, covering audience restrictions, timestamp checks, and replay prevention for secure identity flows.

By Ashish KumarPart 6 of SAML 2.0 for Engineers

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:

  1. Locate the Signature: Find the <ds:Signature> element within the <Assertion> block.
  2. 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 the KeyDescriptor for signing.
  3. 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:

  1. Strict Check: Verify that current_time >= NotBefore and current_time <= NotOnOrAfter.
  2. Tolerance Window: Apply a configurable tolerance (typically ±5 minutes) to account for NTP drift.
    • Effective NotBefore = NotBefore - 5 minutes
    • Effective NotOnOrAfter = NotOnOrAfter + 5 minutes

Warning: Do not ignore the notonorafter timestamp. 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:

  1. Extract the ID: Parse the ID attribute from the <Assertion> root element.
  2. Check Cache: Query your cache (Redis, Memcached, or in-memory LRU) for this ID.
  3. Reject if Found: If the ID exists, reject the request. The assertion has already been consumed.
  4. Store if New: If the ID is not found, store it in the cache.
  5. TTL Alignment: Set the cache TTL to match the assertion’s NotOnOrAfter time. 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:

  1. Signature: Verify against IdP metadata public key.
  2. Audience: Confirm your SP’s EntityID is in <AudienceRestriction>.
  3. Time: Check NotBefore and NotOnOrAfter with clock skew tolerance.
  4. Replay: Ensure the assertion ID has 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