UprootSecurityUprootSecurity

Phase 2 · SAML, OIDC, and OAuth 2.0 · Lesson 1 of 4

SAML 2.0 Deep Dive

Article

·

20 min

·

+10 pts

SAML 2.0 — Security Assertion Markup Language — is the protocol that powers enterprise single sign-on for thousands of organizations. It was ratified in 2005 and remains the dominant federation protocol in enterprises with legacy applications, on-premises infrastructure, and compliance requirements that predate the modern web. If you audit a company with more than a few hundred employees, you will encounter SAML. Probably in the first week.

This lesson breaks down how SAML works, what the assertion looks like, what a GRC engineer needs to verify in a SAML configuration, and where the protocol's known vulnerabilities live.

Explaining OAuth flows to a PM

What SAML does and where it lives

SAML solves a specific problem: how does a user authenticate once with their corporate identity provider and then access multiple applications without logging in again? Before SAML, every application maintained its own user database and its own login page. Employees had dozens of passwords. IT had no centralized way to disable access when someone left. Auditors had no single source of truth for authentication events.

SAML replaced that chaos with a trust model. The identity provider (IdP) handles authentication. The service provider (SP) — the application — trusts the IdP's signed assertion as proof of identity. The user authenticates once, and every SAML-connected application accepts that authentication without seeing the user's password.

SAML is XML-based, verbose, and browser-dependent. It is not used for mobile apps, SPAs, or machine-to-machine communication. But for enterprise SSO — connecting Salesforce, Workday, ServiceNow, AWS, and hundreds of other applications to a corporate identity provider — SAML remains the standard. Many enterprise SaaS applications support SAML but not OIDC. Some support both. A few legacy applications only support SAML.

The three roles

Every SAML interaction involves three actors:

  • Principal — the user attempting to access an application. The principal never sees the SAML XML or knows the protocol is running. They click a link, get redirected, authenticate, and land in the application.
  • Identity Provider (IdP) — the system that authenticates the user and generates the SAML assertion. Okta, Entra ID, Google Workspace, Ping Identity, OneLogin — these are all IdPs in SAML terminology.
  • Service Provider (SP) — the application that receives and validates the SAML assertion. Salesforce, AWS, Slack, Jira — any application configured for SAML SSO is acting as a service provider.

The trust relationship between IdP and SP is established out-of-band before any SSO flow occurs. The IdP provides its metadata (including its signing certificate) to the SP. The SP provides its metadata (including its Assertion Consumer Service URL) to the IdP. This metadata exchange is the configuration step that makes SAML work — and the step where misconfigurations cause the most audit findings.

SP-initiated SSO: The standard flow

The most common SAML flow is SP-initiated, meaning the user starts at the application (service provider) rather than at the identity provider. Here is what happens step by step.

User (Browser)              Service Provider (SP)         Identity Provider (IdP)
     │                              │                              │
1.   │──── Visits SP ─────────────►│                              │
     │    (e.g., salesforce.com)    │                              │
     │                              │                              │
2.   │                              │── Generates AuthnRequest ──►│
     │◄─── 302 Redirect ───────────│   (HTTP Redirect binding)    │
     │     to IdP with             │                              │
     │     SAMLRequest param        │                              │
     │                              │                              │
3.   │──── Browser follows ────────────────────────────────────►  │
     │     redirect to IdP          │                              │
     │                              │                              │
4.   │                              │              IdP shows login │
     │◄─────────────────────────────────── Login page ────────────│
     │                              │                              │
5.   │──── User enters credentials ────────────────────────────►  │
     │     (+ MFA if required)      │                              │
     │                              │                              │
6.   │                              │         IdP validates creds, │
     │                              │         generates assertion  │
     │◄──────────────────────────────── HTML form with ───────────│
     │                              │   SAMLResponse (POST)        │
     │                              │                              │
7.   │──── Browser auto-POSTs ────►│                              │
     │     SAMLResponse to          │                              │
     │     SP's ACS URL             │                              │
     │                              │                              │
8.   │                              │── Validates assertion:       │
     │                              │   - Check XML signature      │
     │                              │   - Check audience           │
     │                              │   - Check NotOnOrAfter       │
     │                              │   - Extract NameID           │
     │                              │                              │
9.   │◄─── Session created, ────────│                              │
     │     user is logged in        │                              │

SP-initiated SAML SSO flow: the user starts at the application, is redirected to the IdP for authentication, and is returned to the application with a signed assertion

The critical detail is step 6: the IdP sends the SAML response back to the browser as an HTML form that auto-submits via POST to the SP's Assertion Consumer Service (ACS) URL. This is the POST binding. The browser is the transport mechanism — the IdP and SP never communicate directly. This matters for network architecture: there is no server-to-server call, which means SAML works even when the SP is behind a firewall that the IdP cannot reach.

What a SAML assertion looks like

The SAML assertion is an XML document embedded inside the SAML response. It contains the claims that the IdP is making about the authenticated user. Here is a representative example:

<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
                ID="_a]8b7c6d5e4f3a2b1c0d9e8f7"
                IssueInstant="2026-05-27T14:30:00Z"
                Version="2.0">
  <saml:Issuer>https://idp.example.com/saml/metadata</saml:Issuer>
  <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <!-- RSA-SHA256 signature over the assertion -->
    <ds:SignedInfo>
      <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
      <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
    </ds:SignedInfo>
    <ds:SignatureValue>base64-encoded-signature...</ds:SignatureValue>
  </ds:Signature>
  <saml:Subject>
    <saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
      alice@example.com
    </saml:NameID>
    <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
      <saml:SubjectConfirmationData
        NotOnOrAfter="2026-05-27T14:35:00Z"
        Recipient="https://app.example.com/saml/acs"
        InResponseTo="_request123"/>
    </saml:SubjectConfirmation>
  </saml:Subject>
  <saml:Conditions NotBefore="2026-05-27T14:29:30Z"
                   NotOnOrAfter="2026-05-27T14:35:00Z">
    <saml:AudienceRestriction>
      <saml:Audience>https://app.example.com</saml:Audience>
    </saml:AudienceRestriction>
  </saml:Conditions>
  <saml:AuthnStatement AuthnInstant="2026-05-27T14:30:00Z"
                       SessionIndex="_session_abc123">
    <saml:AuthnContext>
      <saml:AuthnContextClassRef>
        urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
      </saml:AuthnContextClassRef>
    </saml:AuthnContext>
  </saml:AuthnStatement>
  <saml:AttributeStatement>
    <saml:Attribute Name="firstName">
      <saml:AttributeValue>Alice</saml:AttributeValue>
    </saml:Attribute>
    <saml:Attribute Name="lastName">
      <saml:AttributeValue>Chen</saml:AttributeValue>
    </saml:Attribute>
    <saml:Attribute Name="department">
      <saml:AttributeValue>Engineering</saml:AttributeValue>
    </saml:Attribute>
    <saml:Attribute Name="groups">
      <saml:AttributeValue>Production-Access</saml:AttributeValue>
      <saml:AttributeValue>AWS-DevOps</saml:AttributeValue>
    </saml:Attribute>
  </saml:AttributeStatement>
</saml:Assertion>

Every element in this assertion matters for audit purposes. The Issuer identifies which IdP generated the assertion. The NameID is the user's identity claim — typically their email address. The Conditions block constrains when and where the assertion is valid. The AuthnStatement describes how the user authenticated. The AttributeStatement carries additional claims — department, groups, roles — that the SP can use for authorization decisions.

GRC Engineer's focus

When reviewing a SAML configuration during an audit or assessment, verify these specific elements:

Assertion signing — The assertion must be signed with RSA-SHA256 or stronger. RSA-SHA1 is deprecated and should be flagged. Check both the IdP configuration (what algorithm it uses to sign) and the SP configuration (whether it requires signed assertions or accepts unsigned ones).

Encryption — In sensitive environments, the assertion should also be encrypted (not just signed). Signing prevents tampering; encryption prevents the browser or any intermediary from reading the assertion contents. Most enterprise deployments sign but do not encrypt — flag this for applications handling PII or PHI.

Audience restriction — The AudienceRestriction must match the SP's entity ID exactly. Without this, an assertion generated for one SP could be replayed against a different SP that trusts the same IdP.

Session timeout — The NotOnOrAfter window should be short (typically 3-5 minutes). A wide window increases the replay attack surface. Also check the SP's session duration — how long does the local session last after the assertion is consumed?

NameID format — The NameID format should be appropriate for the use case. emailAddress is common but can break if users change email addresses. persistent is more stable for long-term identity correlation.

IdP-initiated SSO and why it is less secure

There is a second SAML flow: IdP-initiated SSO. In this flow, the user starts at the IdP (for example, clicks an application tile in the Okta dashboard), and the IdP generates a SAML assertion without having received an AuthnRequest from the SP.

This flow is simpler to implement but introduces a significant security weakness: because there is no AuthnRequest, there is no InResponseTo value in the assertion. The SP cannot verify that the assertion was generated in response to a specific authentication request. This makes IdP-initiated flows more vulnerable to replay attacks — an attacker who captures an assertion can resubmit it to the SP's ACS URL, and the SP has no request ID to validate against.

Most compliance frameworks do not explicitly require SP-initiated over IdP-initiated SSO, but security best practice is to prefer SP-initiated flows. When you see IdP-initiated SSO in a configuration, note it as a risk and recommend migrating to SP-initiated where the SP supports it.

Quick check

In an SP-initiated SAML flow, the IdP sends the SAML response back to the SP via which mechanism?

Common SAML vulnerabilities

SAML has been in production for over two decades. Its vulnerabilities are well-documented, and a GRC engineer needs to know them because they directly affect audit findings and risk assessments.

XML signature wrapping

This is the most critical SAML vulnerability class. XML signature wrapping attacks exploit the fact that XML documents can be restructured while preserving the signed portion. An attacker modifies the assertion to change the NameID (for example, from attacker@example.com to admin@example.com) while moving the original signed assertion to a different location in the XML tree. If the SP validates the signature against the original signed element but reads the identity from the attacker-modified element, the attacker logs in as the victim.

The mitigation is strict XML parsing and validation. Modern SAML libraries handle this correctly, but misconfigured or outdated libraries may be vulnerable. When assessing an SP's SAML implementation, ask what library is used and whether it has been updated to address known signature wrapping CVEs.

Replay attacks

A captured SAML assertion can be resubmitted if the SP does not validate the timing constraints. The NotOnOrAfter condition sets an expiration time on the assertion. If the SP does not enforce this condition — or if the window is too wide — an attacker who intercepts the assertion (through network sniffing, browser history, or log files) can replay it to gain access.

Mitigation: enforce short NotOnOrAfter windows (3-5 minutes), validate that the assertion has not been used before (one-time use enforcement), and require HTTPS for all SAML endpoints so assertions are encrypted in transit.

Open redirect in RelayState

The RelayState parameter in SAML tells the SP where to redirect the user after authentication succeeds. If the SP does not validate the RelayState value, an attacker can craft a SAML flow that redirects the user to a malicious site after authentication — potentially to a phishing page that captures additional credentials or session tokens.

Mitigation: the SP must validate that the RelayState URL belongs to an allowed domain before redirecting. This is a simple check that is often missing in custom SAML implementations.

Quick check

An auditor discovers that a service provider's SAML configuration accepts unsigned assertions from the IdP. What is the primary risk?

What to carry forward

SAML 2.0 is the enterprise SSO workhorse. It is XML-based, browser-dependent, and showing its age — but it remains deeply embedded in enterprise infrastructure and will not disappear anytime soon. As a GRC engineer, you need to read SAML assertions, verify signing and encryption configurations, understand the SP-initiated flow well enough to explain it in a control narrative, and flag the known vulnerability classes when you encounter weak configurations.

The next lesson covers OIDC and OAuth 2.0 — the modern protocols that handle the use cases SAML cannot: mobile apps, SPAs, APIs, and machine-to-machine communication.

SAML 2.0 Deep Dive — UprootSecurity Bootcamp