Aller au contenu
medium

SAML 2.0 : la fédération enterprise

16 min de lecture

SAML 2.0 reste le protocole de fédération dominant en entreprise. Même si OIDC est plus moderne, la majorité des applications SaaS enterprise (Salesforce, ServiceNow, Workday...) supportent SAML en premier. Si vous intégrez des applications d'entreprise, vous rencontrerez SAML. Ce guide vous donne les clés pour le comprendre, le configurer et le déboguer.

Ce guide vous explique SAML 2.0, le protocole de fédération dominant dans les grandes entreprises. À la fin, vous saurez :

  • Comprendre pourquoi SAML reste omniprésent malgré OIDC
  • Distinguer les flux SP-initiated et IdP-initiated
  • Configurer les assertions, signatures et mapping d'attributs
  • Diagnostiquer les erreurs SAML les plus courantes

Prérequis : avoir lu OpenID Connect.

SAML 2.0 date de 2005. Pourquoi est-il encore partout ?

Réponse courte : parce que les grandes applications enterprise (Salesforce, ServiceNow, Workday, SAP...) le supportent depuis des années, et qu'il fonctionne.

Le choix ne vous appartient qu'à moitié : dans la plupart des intégrations, c'est l'application cible qui impose son protocole. Ce tableau sert donc surtout à trancher les cas où vous avez réellement la main, typiquement une application que vous développez. Retenez la logique générale : SAML pour l'existant enterprise et le B2B, OIDC pour tout ce qui est neuf, OAuth 2.0 dès qu'il s'agit d'autoriser un accès API plutôt que d'authentifier une personne.

SituationProtocole recommandé
L'application ne supporte que SAMLSAML
Intégration B2B avec un partenaire SAMLSAML
Nouvelle application web/mobileOIDC
API à sécuriserOAuth 2.0

Comprendre ces différences vous aide à choisir le bon protocole selon votre contexte. SAML reste incontournable en entreprise, tandis qu'OIDC domine les architectures modernes.

AspectSAMLOIDC
Année20052014
FormatXMLJSON/JWT
Taille des messagesVerbeux (~5-15 KB)Compact (~1-2 KB)
SignatureXML-DSig + certificatsJWS (signature JWT)
ChiffrementXML-Enc (optionnel)JWE (optionnel)
ConfigurationÉchange de fichiers metadataDiscovery endpoint
Mobile/SPADifficileNatif
Enterprise legacyExcellent supportVariable
DebuggingComplexe (XML, signatures)Simple (jwt.io, JSON)

Trois acteurs interviennent dans chaque échange SAML. Connaître leur rôle est essentiel pour déboguer les problèmes de fédération.

ActeurRôleÉquivalent OIDC
Identity Provider (IdP)Authentifie l'utilisateur, émet les assertionsOpenID Provider
Service Provider (SP)Application qui consomme les assertionsRelying Party
PrincipalL'utilisateur qui s'authentifieEnd-User

SAML définit deux points de départ possibles pour une connexion, et ils ne se valent pas en matière de sécurité. La différence tient à un seul détail : l'existence ou non d'une AuthnRequest émise par le Service Provider. Quand elle existe, le SP peut vérifier que la réponse reçue correspond bien à une demande qu'il a lui-même initiée, via l'attribut InResponseTo. Sans elle, cette vérification est impossible.

L'utilisateur commence par l'application (Service Provider).

  1. Accès à l'application

    L'utilisateur tente d'accéder à https://salesforce.example.com

  2. Génération de l'AuthnRequest

    Le SP génère une demande d'authentification SAML (AuthnRequest).

  3. Redirection vers l'IdP

    Le navigateur est redirigé vers l'IdP avec l'AuthnRequest.

  4. Authentification

    L'IdP authentifie l'utilisateur (login, MFA...).

  5. Génération de l'assertion

    L'IdP crée une assertion SAML signée contenant l'identité.

  6. Retour au SP (POST)

    Le navigateur envoie l'assertion au SP (généralement via HTTP-POST).

  7. Validation et session

    Le SP valide la signature et crée une session pour l'utilisateur.

Avantages :

  • L'application contrôle le flux
  • Protection CSRF native (le SP génère un ID de requête)

L'utilisateur commence par le portail IdP.

  1. Accès au portail IdP

    L'utilisateur accède au portail enterprise (ex: Okta dashboard).

  2. Clic sur l'application

    L'utilisateur clique sur l'icône de l'application.

  3. Génération directe de l'assertion

    L'IdP génère une assertion SAML sans AuthnRequest préalable.

  4. Envoi au SP

    L'assertion est envoyée directement au SP.

  5. Validation et session

    Le SP valide et crée la session.

Risques :

  • Pas d'AuthnRequest = pas de InResponseTo à valider
  • Risque de CSRF si le RelayState n'est pas vérifié

Une assertion SAML est un document XML signé qui contient :

  • L'identité de l'utilisateur (NameID)
  • Les attributs (email, groupes, rôles...)
  • Les conditions de validité (dates, audience)
  • La signature de l'IdP

Voici une assertion réduite à ses éléments porteurs de sens ; en production elle fait plusieurs kilo-octets, dont l'essentiel est occupé par le bloc de signature encodé en base64.

<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_abc123" IssueInstant="2024-02-06T10:00:00Z">
<!-- Qui a émis l'assertion -->
<saml:Issuer>https://idp.example.com</saml:Issuer>
<!-- Signature XML -->
<ds:Signature>...</ds:Signature>
<!-- Subject : l'utilisateur authentifié -->
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">
alice@example.com
</saml:NameID>
</saml:Subject>
<!-- Conditions de validité -->
<saml:Conditions NotBefore="2024-02-06T10:00:00Z"
NotOnOrAfter="2024-02-06T10:05:00Z">
<saml:AudienceRestriction>
<saml:Audience>https://salesforce.example.com</saml:Audience>
</saml:AudienceRestriction>
</saml:Conditions>
<!-- Contexte d'authentification -->
<saml:AuthnStatement AuthnInstant="2024-02-06T10:00:00Z">
<saml:AuthnContext>
<saml:AuthnContextClassRef>
urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport
</saml:AuthnContextClassRef>
</saml:AuthnContext>
</saml:AuthnStatement>
<!-- Attributs -->
<saml:AttributeStatement>
<saml:Attribute Name="email">
<saml:AttributeValue>alice@example.com</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="groups">
<saml:AttributeValue>developers</saml:AttributeValue>
<saml:AttributeValue>admins</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>

Ce sont les six balises que vous regarderez en priorité lors d'un dépannage. Trois d'entre elles décident à elles seules de l'acceptation ou du rejet : Conditions pour la fenêtre temporelle et le destinataire, Signature pour l'authenticité, NameID pour la correspondance avec un compte existant côté application.

ÉlémentDescription
IssuerURL de l'IdP qui a émis l'assertion
NameIDIdentifiant principal de l'utilisateur
ConditionsPériode de validité, audience autorisée
AuthnStatementPreuve de l'authentification
AttributeStatementAttributs additionnels (email, groupes...)
SignatureSignature XML de l'IdP

SAML utilise XML-DSig pour signer les assertions (et optionnellement les chiffrer).

Une réponse SAML contient deux enveloppes signables : la Response et l'Assertion qu'elle transporte. Signer la seule Response laisse la porte ouverte aux attaques par substitution d'assertion, où un attaquant remplace le contenu utile en conservant l'enveloppe signée. La signature de l'assertion est donc obligatoire, celle de la réponse fortement recommandée.

ÉlémentSigné par
AssertionL'IdP (obligatoire)
ResponseL'IdP (recommandé)
AuthnRequestLe SP (optionnel)

Le certificat de signature de l'IdP est copié dans la configuration de chaque Service Provider. C'est ce qui rend sa rotation délicate : le changer d'un côté casse tous les autres tant qu'ils n'ont pas récupéré le nouveau. La parade consiste à publier les deux certificats simultanément dans les metadata avant de basculer la signature.

Le SP doit :

  1. Obtenir le certificat public de l'IdP (via metadata ou import manuel)
  2. Valider la signature de l'assertion
  3. Valider la chaîne de certificats (optionnel selon la config)

Les fichiers metadata contiennent la configuration SAML d'un IdP ou d'un SP.

Le metadata d'un IdP publie deux informations que le SP ne peut pas deviner : le certificat public qui servira à valider les signatures, et l'URL du service SingleSignOnService vers laquelle rediriger l'utilisateur.

<EntityDescriptor entityID="https://idp.example.com">
<IDPSSODescriptor>
<KeyDescriptor use="signing">
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>MIIC...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>
<SingleSignOnService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
Location="https://idp.example.com/sso"/>
</IDPSSODescriptor>
</EntityDescriptor>

Côté SP, l'élément déterminant est l'AssertionConsumerService : c'est l'URL exacte où l'IdP postera l'assertion, et la moindre différence de casse ou de slash final provoque un rejet. L'entityID déclaré ici doit par ailleurs correspondre exactement à la valeur que l'IdP place dans l'AudienceRestriction.

<EntityDescriptor entityID="https://salesforce.example.com">
<SPSSODescriptor>
<KeyDescriptor use="signing">
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>MIID...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>
<AssertionConsumerService
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Location="https://salesforce.example.com/saml/acs"
index="0"/>
</SPSSODescriptor>
</EntityDescriptor>

L'échange est bidirectionnel : chaque partie doit connaître l'autre, et une configuration à sens unique produit une erreur au moment du retour vers l'application. Quand l'IdP publie une URL de metadata plutôt qu'un fichier, préférez-la : le SP recharge alors le certificat automatiquement lors des rotations.

  1. Exporter le metadata de l'IdP

  2. Importer dans le SP (ou donner l'URL du metadata)

  3. Exporter le metadata du SP

  4. Importer dans l'IdP (créer un "realm" ou "application")

  5. Configurer le mapping d'attributs

Les attributs SAML ne sont pas standardisés. Chaque vendor utilise ses propres noms.

Le NameID est la clé sur laquelle le SP fait correspondre l'assertion à un compte local, et son format doit être identique des deux côtés. L'adresse e-mail est le choix le plus répandu mais aussi le plus fragile : elle change quand la personne se marie ou change de service, et l'utilisateur perd alors l'accès à son compte. Le format persistent évite ce problème en fournissant un identifiant opaque et stable par couple utilisateur/application.

FormatValeurUsage
emailAddressalice@example.comLe plus courant
persistentID opaque stableRecommandé pour la sécurité
transientID temporaireSessions anonymes
unspecifiedQuelconqueÀ éviter

Trois correspondances reviennent dans presque toutes les intégrations. Elles illustrent le même travail : la colonne de gauche est le nom imposé par l'application, celle du milieu le nom que porte l'attribut dans votre annuaire, et le mapper configuré sur l'IdP fait la traduction au moment d'émettre l'assertion. Un attribut absent de cette configuration ne figurera tout simplement pas dans l'AttributeStatement.

Ce que le SP attendCe que l'IdP aSolution
User.EmailemailMapper emailUser.Email
memberOfgroupsMapper groupsmemberOf
firstNamegivenNameMapper givenNamefirstName

Déboguer SAML est plus ingrat qu'OIDC parce que les messages transitent par des redirections du navigateur et non par des appels serveur à serveur : ils n'apparaissent dans aucun journal applicatif. La méthode consiste donc toujours à capturer l'échange dans le navigateur, à décoder l'assertion, puis à comparer son contenu à ce que le SP attend.

Ces quatre sources se complètent : les deux premières donnent le contenu réel de l'échange, les deux dernières le verdict de chaque partie. Attention avec les décodeurs en ligne : une assertion contient des données d'identité, ne collez pas une assertion de production sur un service tiers.

OutilUsage
SAML-tracer (extension navigateur)Capturer les requêtes/réponses SAML
samltool.comDécoder, valider, générer des assertions
Keycloak logsErreurs côté IdP
Logs applicatifsErreurs côté SP

Les six messages ci-dessous représentent la quasi-totalité des échecs constatés en intégration. Ils partagent un point commun utile au diagnostic : ils viennent tous d'un désaccord de configuration entre l'IdP et le SP, pas d'un défaut du protocole. Repérez le message exact dans les journaux du SP, la colonne du milieu vous dit de quel côté chercher.

SymptômeCause probableSolution
Invalid signatureCertificat mal importé, mauvais certificatRé-importer le certificat de l'IdP
Assertion expiredClock skew (décalage d'horloge)Synchroniser NTP sur tous les serveurs
Invalid audienceAudience ne correspond pas à l'entityID du SPVérifier la config côté IdP
ACS URL mismatchL'URL de retour ne correspond pasVérifier AssertionConsumerService dans les metadata
Missing attributeAttribut non mappéConfigurer le mapper côté IdP
NameID format mismatchLe SP attend un format différentAligner les formats NameID

Suivez ces six vérifications dans l'ordre : elles vont du plus général au plus précis, et chacune élimine une famille de causes. La première est incontournable, sans capture du trafic vous en serez réduit aux hypothèses.

  1. Capturer le trafic avec SAML-tracer

  2. Décoder la Response sur samltool.com

  3. Vérifier les timestamps (NotBefore, NotOnOrAfter)

  4. Vérifier l'Audience

  5. Valider la signature (certificat correct ?)

  6. Vérifier les attributs présents dans l'assertion

La sécurité de SAML repose entièrement sur la validation faite par le Service Provider. L'IdP a beau signer correctement, une bibliothèque SP qui accepte une assertion sans vérifier sa signature, son audience ou sa fenêtre de validité annule toute la protection. Historiquement, la plupart des vulnérabilités SAML publiées portaient sur des défauts de validation côté SP, pas sur le protocole.

Ce tableau se lit comme une liste de contrôles à confirmer sur votre implémentation, pas comme un catalogue théorique. Pour chaque ligne, la question à poser à l'équipe qui exploite le SP est simple : cette vérification est-elle activée, et l'avez-vous testée en la faisant échouer volontairement ?

RisqueMitigation
Signature bypassToujours valider la signature de l'assertion ET de la response
XML injectionParser XML sécurisé, désactiver les entity references
Replay attackVérifier InResponseTo, tracker les assertion IDs consommées
IdP-initiated CSRFValider RelayState, préférer SP-initiated
Assertion interceptionHTTPS obligatoire, chiffrement optionnel

Ces cinq réglages sont ceux à appliquer par défaut sur toute nouvelle intégration. Le plus souvent négligé est la durée de validité : une assertion valable une heure reste rejouable pendant une heure si le SP ne mémorise pas les identifiants déjà consommés.

  • Signez les assertions ET les responses
  • Chiffrez les assertions si elles contiennent des données sensibles
  • Validez l'audience (AudienceRestriction)
  • Limitez la durée de validité des assertions (< 5 min)
  • Utilisez SP-initiated quand possible

Là où le tableau de dépannage listait des messages d'erreur, celui-ci recense les défauts d'exploitation qui n'émettent aucune alerte tant que le SSO fonctionne. Ils se manifestent tous de la même façon : une panne brutale un matin, sans changement apparent la veille. La colonne de droite décrit à chaque fois une mesure préventive, à mettre en place avant l'incident.

ErreurConséquenceSolution
Certificat expiré non détectéSSO casséAlerting sur les dates d'expiration
Pas de validation InResponseToVulnérable au replayValider l'ID de la requête originale
Clock skew > 5 minAssertions toujours rejetéesNTP sur tous les serveurs
Metadata pas à jourÉchecs intermittentsURL de metadata + refresh automatique

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn