Skip to content
All articles
Protocols17 August 2026·9 min read

The SAML signature checks that get skipped

Every library will tell you the signature verified. That sentence has at least four meanings, and only one of them is safe.

Paycux security engineering

The signature on a SAML assertion is the entire trust model. There is no channel binding, no shared secret, no session between you and the identity provider. A browser hands you a document and you decide whether to believe it, and that decision is a signature check against a certificate you were given months ago.

Which is why the interesting failures are not cryptographic. Nobody forges an RSA signature. They arrange for you to verify a valid signature over a document that is not the one you then read.

Signed what, exactly

A SAML response can be signed at the response level, at the assertion level, at both, or at neither. A library that reports success may have verified a signature on the outer response while your code reads attributes from an assertion that was never covered by it. That gap is the whole family of signature wrapping attacks: keep the signed element intact so verification passes, add a second unsigned element nearby, and rely on the reader picking the wrong one.

The defence is to make verification and reading operate on the same node rather than on the same document. Verify a specific element, keep a reference to that exact element, and read attributes only from it. If your code path is verify(document) followed by document.find('Assertion'), the second call can find something the first never checked.

  • Require a signature on the assertion, not merely somewhere in the response
  • Read attributes from the verified node, never from a fresh search of the document
  • Refuse documents containing more than one assertion — legitimate ones do not
  • Reject unsigned responses outright rather than falling back to a lenient path

Nobody forges the signature. They arrange for you to verify a real one over a document you do not read.

Trust the certificate you configured

An assertion carries the signing certificate inside it. Verifying against that certificate is self-consistent and completely meaningless: anyone can sign a document and attach their own key. The certificate that matters is the one exchanged when the connection was configured, held on your side, and compared before anything else happens.

Handle rotation deliberately rather than by relaxing the check. Providers roll certificates, so a connection should be able to hold more than one trusted certificate at a time and accept a signature from either during the overlap. That is a configuration change with an expiry date attached, not a permanent tolerance for unknown keys.

The checks that come after

A valid signature says the document was minted by that identity provider. It says nothing about whether it was minted for you, for now, or for this particular sign-in attempt. Those are separate checks and every one of them has been the root cause of a real incident somewhere.

Keep an assertion identifier cache for the validity window as well. Timestamps alone allow the same assertion to be replayed several times within its own lifetime, which is a narrow window but not a theoretical one when the document travels through a browser you do not control.

  • Audience must name your entity identifier, exactly, with no substring matching
  • Destination and Recipient must match the endpoint the document arrived at
  • NotBefore and NotOnOrAfter, with a skew allowance measured in seconds
  • InResponseTo must match a request you issued, for service-provider-initiated flows
  • Assertion identifier stored until expiry, so a replay inside the window is refused
  • Pin the digest and signature algorithms; do not accept whatever the document proposes

Everything here, already built

Sign-in, enterprise SSO, directory provisioning, roles and an audit trail behind one API. Start with the quickstart and have a working sign-in this afternoon.

Start selling to enterprise customers

Create an account, point sign-in at Paycux, and get back to the part of the product that is actually yours.