SAML 2.0¶
SAML 2.0 is still what a large part of the installed base speaks, and an identity provider that cannot answer it cannot replace the one a company already has. Beffroi serves it in both roles, on the tenant, the people and the sessions OpenID Connect uses: one person of the core, two protocols to reach them. A browser signed in by one is signed in for the other, and a session that ends ends for both.
The two roles¶
| Role | What it is for | Turned on by |
|---|---|---|
| Identity provider | An application that speaks SAML signs its users in against Beffroi: a SimpleSAMLphp or Shibboleth service provider, a product that asks for a SAML identity provider, Microsoft Entra ID federating its guests | beffroi.saml.identity_provider |
| Service provider | Beffroi signs a person in against the SAML identity provider of their organisation, found by the domain of their address, and then answers the OpenID Connect client or the page that was waiting | beffroi.saml.service_provider |
graph LR
sp["An application<br/><small>SAML service provider</small>"] -- "AuthnRequest" --> b["Beffroi"]
rp["A client<br/><small>OpenID Connect</small>"] -- "authorization request" --> b
b -- "AuthnRequest" --> up["The identity provider<br/>of an organisation<br/><small>upstream</small>"]
Both roles are off by default, and each is a switch of its own. A deployment that turns neither on draws no SAML key and publishes no metadata, and every message that reaches its single sign-on or single logout service is refused, nobody being declared to send one: nothing of this section costs anything to a provider that does not use it.
The peers themselves, the service providers on one side and the upstream identity providers on the other, are declared in the same section, keyed by their entity ID. They are configuration and nothing else: no request registers one and no page edits one.
What is served¶
Paths are relative to the issuer, as every route of the bundle.
| Method | Path | Role | What it is |
|---|---|---|---|
GET |
/saml/metadata |
identity provider | The metadata of the identity provider. Its URL is its entity ID |
GET POST |
/saml/sso |
identity provider | Single sign-on, over HTTP-Redirect and HTTP-POST at the one URL |
GET POST |
/saml/slo |
identity provider | Single logout, over both bindings, a request or an answer |
GET |
/saml/sp/metadata |
service provider | The metadata of the service provider. Its URL is its entity ID unless the deployment names another |
GET |
/saml/sp/metadata/{identity provider} |
service provider | The same metadata with the encryption certificates meant for one upstream identity provider |
POST |
/saml/sp/acs |
service provider | The assertion consumer service, over HTTP-POST |
A role the deployment did not turn on serves nothing. The metadata of that role answer 404, and so do the
sign-on and the assertion consumer service of the service provider; the single sign-on and single logout
services of the identity provider refuse every message, as one of an issuer nobody declared. The
endpoints lists the routes behind them, the ones a login sends a browser
back through included.
What is always true¶
None of the rules below has an option. They are properties of the code, held by its tests, and a peer that needs one of them relaxed is a peer this provider does not serve.
| Rule | Why |
|---|---|
| Every assertion the identity provider writes is signed, and every response over it, under RSA-SHA256. Every assertion the service provider takes is covered by a signature, its own or its response's | A service provider that verifies either finds a signature, an assertion stands alone as the statement of the tenant, and nothing unsigned is read as a statement of anybody |
| SHA-1, MD5, HMAC, DSA and ECDSA are refused, in an XML signature and over the query string of HTTP-Redirect | A verifier that honours the algorithm a message names verifies what the attacker made verifiable |
| An assertion sent encrypted is encrypted under AES-256-GCM, its key under RSA-OAEP. Nothing is ever encrypted in CBC | CBC authenticates nothing, which is what padding oracles are made of |
| A message is read only in what a signature covers | Signature wrapping hands the verifier one element and the reader another |
A peer's keys come from its metadata, never from the KeyInfo of a message |
A message that names the key it is verified with verifies under the attacker's key |
| Every assertion and every logout message is accepted once | A message taken off the wire is not a second login, nor a second logout |
A peer is declared by the deployment, and its metadata come from a file of the deployment or an https URL, no redirect followed |
A peer registered by a request, or metadata fetched wherever a redirect points, is a peer an attacker chose |
Every endpoint a browser is sent to is https on a host |
An assertion posted in clear, or to a javascript: URL, is an assertion given away |
| A response nobody asked for is refused, and no assertion is sent unasked | Nothing binds an unsolicited response to the browser that carries it, which is how a victim is signed in to an attacker's account |
The XML underneath goes through one hardened loader, which refuses a document type declaration, an entity, two elements under the same identifier and a document past its bounds before anything reads it, and a SAML message, a metadata document or a signature is never parsed any other way. The SAML packages says what each of the libraries guarantees.
What is not served¶
- The Artifact and SOAP bindings, SAML 1.x and WS-Federation. A single logout reaches a service provider through the browser that ended the session, never server to server.
- Metadata aggregates of an inter-institution federation: an
md:EntitiesDescriptoris refused by name, and each peer is declared on its own. - An administration interface for the peers. They are configuration, and the console reads and refreshes their metadata.
- Single logout with an upstream identity provider, which the metadata of the service provider do not announce.
- Sign-on started by an identity provider. Every assertion the identity provider writes answers an
AuthnRequest, and every response the service provider takes answers a request it sent. - The groups an upstream identity provider asserts are memberships of the tenant. No claim hands them to a client of OpenID Connect.
In this section¶
| Page | What it covers |
|---|---|
| The identity provider | Turning the role on, its key, its certificate and its metadata |
| Service providers | Declaring one: its metadata, the attributes it receives, the signature its requests need, the encryption it asks for |
| Single sign-on | What a request is answered with, the session it reuses and the assertion it is given |
| Single logout | A logout a service provider asks for, and the one the provider tells every service provider of a session about |
| Upstream identity providers | The tenant as a service provider: its metadata, the identity providers it trusts and what it reads of their assertions |
| Signing in through an organisation | The login page that asks for the address first, the person created at the first sign-in, and the session it opens |
| Microsoft Entra ID | Entra federating its guests to Beffroi, and Entra as the upstream identity provider of a tenant |
| The SAML packages | xml-security, saml2-model and saml2-bindings, which carry the format, and what each one guarantees |