SAML 2.0 — Comment une application peut imposer un niveau de sécurité ? [2/3]

Gregory EVE
Smile with WSO2
Published in
7 min readNov 21, 2019

Une application, compatible SAML 2.0, dite sensible peut vouloir s’assurer qu’une authentification réalisée par un serveur d’authentification respecte ses besoins en termes de degré de confiance concernant la personne qui va utiliser ses services.

Cette série, “Comment une application peut imposer un niveau de sécurité ?” est composée de 3 parties :

1. LoA — Level of Assurance

2. SAML 2.0 AuthnContext

3. OpenID Connect ACR : [Bientôt]

Qu’est-ce que c’est qu’un “contexte d’authentification”?

Un contexte d’authentification défini fonctionnellement quelle est la méthodologie mise en oeuvre pour authentifier le demandeur final.

Par exemple, on va définir qu’une personne doit renseigner son identifiant et son mot de passe, et qu’il doit ensuite confirmer son identité via un token envoyé par mail pour un intervenant extérieur, ou via un générateur physique pour les collaborateurs internes.

Fonctionnellement entre le fournisseur d’identité et l’application les échanges se passent comme ceci :

  1. L’application cliente peut lister les contextes qu’elle autorise dans la requête de demande d’authentification et d’autorisation qu’elle réalise auprès du fournisseur d’identité.
  2. Celui-ci sélectionne un de ces contextes, selon des règles préétablies et partagées. Puis, il réalise le processus d’authentification du demandeur final, et fourni dans sa réponse à l’application le contexte d’authentification utilisé.
  3. L’application cliente est ensuite libre d’accepter ou non la réponse reçue.

Que fourni le standard SAML 2.0 ?

La notion de contexte est défini au cœur de la spécification dans le document SAML 2.0 Core.

Contextes Authn

La spécification SAML 2.0 fournis une liste de classes de contexte utilisable dans un document annexe http://docs.oasis-open.org/security/saml/v2.0/saml-authn-context-2.0-os.pdf. Celle-ci n’est pas fixe, vous êtes libre de définir vos propres contextes représentés par une URI.

Quelques exemples de contextes courants :

  • urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport : Username/Password sous SSL/TLS
  • urn:oasis:names:tc:SAML:2.0:ac:classes:X509 : Certificat X.509
  • urn:federation:authentication:windows : Integrated Windows Authentication (IWA)
  • urn:oasis:names:tc:SAML:2.0:classes:Kerberos : Kerberos

Requête SAML 2.0

Au chapitre 3.3.2.2 on retrouve la définition de l’élément <AuthnQuery> qui défini qu’elles sont les assertions d’authentification attendues par l’application. En particulier on va s’intéresser au sous élément optionnel <RequestedAuthnContext> qui défini le contexte d’authentification attendu caractérisé par 2 informations :

  • AuthnContextClassRef : Une ou plusieurs URI nommant les contextes souhaités.
  • Comparison : explicitant comment le paramètre précédent doit être utilisé, les valeurs pouvant être
    - “exact” : le contexte utilisé doit être dans la liste fournie
    - “minimum” : le contexte utilisé doit être au minimum l’un de ceux fournis
    - “maximum” : le contexte utilisé doit être
    - “better” : le contexte utilisé doit être le plus haut possible tout en ne dépassant pas ceux de la liste fournie

Exemple:

<samlp:AuthnRequest
AssertionConsumerServiceURL="http://myapp/acs.jsp"
Destination="https://myIdP/samlsso" ID="IDP"
IssueInstant="2019-06-19T13:51:47Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
ProviderName="SP Example" Version="2.0" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol">
<saml:Issuer>http://myapp</saml:Issuer><samlp:NameIDPolicy AllowCreate="true"
Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"/>
<samlp:RequestedAuthnContext Comparison="exact">
<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:classes:Kerberos</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>
</samlp:AuthnRequest>

Réponse SAML

Au chapitre 2.7.2.2 on retrouve la spécification de l’élément <AuthnContext> d’une réponse SAML 2.0 qui défini l’assertion de contexte d’authentification effectivement mise en oeuvre. Si la requête contient l’élément <RequestedAuthnContext>, celui-ci est obligatoirement présent.

Exemple :

<?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response Destination="http://myapp/acs.jsp"
ID="_606fa48ff09014ac47ff8e0f15432b9d" InResponseTo="IDP"
IssueInstant="2019-06-19T13:52:05.224Z" Version="2.0"
xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://myapp</saml2:Issuer>
[...]
<saml2p:Status><saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/></saml2p:Status>
<saml2:Assertion ID="_9972481ec4d72b00ff38c65ba10d22af" IssueInstant="2019-06-19T13:52:05.236Z"
Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://myapp</saml2:Issuer>
[...]
<saml2:AuthnStatement AuthnInstant="2019-06-19T13:52:05.122Z"
SessionIndex="9ab5e764-ddf4-40df-962d-372a17702c5c">
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:classes:Kerberos</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
[...]
</saml2:Assertion>
</saml2p:Response>

Comment le met-on en oeuvre avec WSO2 Identity Server?

Il est possible de mettre en oeuvre partiellement cette fonctionnalité avec WSO2 IS. Pour cela il vous faudra utiliser l’authentification adaptive et en particulier le template “ACR-based”.

Il vous faudra également les patchs WUM 4886 et 5076, demandé par Smile en juin 2019 (ou utiliser WSO2 IS ≥ 5.9.0 pour une intégration native).

WSO2 IS gère actuellement la lecture du contexte reçu dans la requête SAML 2.0, son traitement via l’authentification adaptive et la création d’une réponse basée sur le contexte reçu dans la requête. Par contre WSO2 IS ne gère pas l’opérateur de comparaison, la réception d’une liste de classe de contexte ou de références, une réponse de contexte personnalisée ou une erreur de contexte non disponible.

Nous allons prendre pour exemple le scénario suivant:

L’application cliente nécessite une authentification pour accéder à son espace utilisateur, mais certaines zones manipulant des données personnelles sont jugées sensibles.

L’application délègue l’authentification à un IAM, WSO2 Identity Server et dialogue avec celui-ci en SAML 2.0

2 contextes d’authentification sont définis :
- urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport : authentification traditionnelle nom d’utilisateur et mot de passe sous TLS
- https://mycompany.me/auth/context/2FA : authentification traditionnelle nom d’utilisateur et mot de passe sous TLS en premier facteur et usage d’un OTP mail en second facteur

Modification des fichiers de configuration

Premièrement il nous faut déclarer un nouvel Identity Provider dans WSO2 IS pour gérer l’OTP. Nous allons utiliser pour cela l’envoi d’un OTP par email selon la documentation suivante : https://docs.wso2.com/display/IS580/Configuring+Email+OTP

notre fichier repository/conf/identity/application-authentication.xml contient:

<AuthenticatorConfig name="EmailOTP" enabled="true">
<Parameter name="EMAILOTPAuthenticationEndpointURL">https://localhost:9443/emailotpauthenticationendpoint/emailotp.jsp</Parameter>
<Parameter name="EmailOTPAuthenticationEndpointErrorPage">https://localhost:9443/emailotpauthenticationendpoint/emailotpError.jsp</Parameter>
<Parameter name="EmailAddressRequestPage">https://localhost:9443/emailotpauthenticationendpoint/emailAddress.jsp</Parameter>
<Parameter name="usecase">association</Parameter>
<Parameter name="useEventHandlerBasedEmailSender">true</Parameter>
<Parameter name="secondaryUserstore">primary</Parameter>
<Parameter name="EMAILOTPMandatory">false</Parameter>
<Parameter name="sendOTPToFederatedEmailAttribute">false</Parameter>
<Parameter name="federatedEmailAttributeKey">email</Parameter>
<Parameter name="EmailOTPEnableByUserClaim">true</Parameter>
<Parameter name="useEventHandlerBasedEmailSender">true</Parameter>
<Parameter name="CaptureAndUpdateEmailAddress">false</Parameter>
<Parameter name="showEmailAddressInUI">true</Parameter>
</AuthenticatorConfig>

nous réalisons de même la configuration à notre serveur de messagerie dans le fichier repository/conf/output-event-adapters.xml

<adapterConfig type="email"><!-- Comment mail.smtp.user and mail.smtp.password properties to support connecting SMTP servers which use trustbased authentication rather username/password authentication --><adapterConfig type="email">
<property key="mail.smtp.from">{{output_adapter.email.from_address}}</property>
<property key="mail.smtp.user">{{output_adapter.email.username}}</property>
<property key="mail.smtp.password">{{output_adapter.email.password}}</property>
<property key="mail.smtp.host">{{output_adapter.email.hostname}}</property>
<property key="mail.smtp.port">{{output_adapter.email.port}}</property>
<property key="mail.smtp.starttls.enable">{{output_adapter.email.enable_start_tls}}</property>
<property key="mail.smtp.auth">{{output_adapter.email.enable_authentication}}</property>
<!-- Thread Pool Related Properties -->
<property key="minThread">8</property>
<property key="maxThread">100</property>
<property key="keepAliveTimeInMillis">20000</property>
<property key="jobQueueSize">10000</property>
</adapterConfig>

et dans le fichier repository/conf/axis2/axis2.xml et nous commentons le module addressing qui est incompatible avec cette configuration.

<!-- <module ref="addressing"/> --><transportSender name="mailto"
class="org.apache.axis2.transport.mail.MailTransportSender">
<parameter name="mail.smtp.from">wso2demomail@gmail.com</parameter>
<parameter name="mail.smtp.user">wso2demomail</parameter>
<parameter name="mail.smtp.password">mailpassword</parameter>
<parameter name="mail.smtp.host">smtp.gmail.com</parameter>
<parameter name="mail.smtp.port">587</parameter>
<parameter name="mail.smtp.starttls.enable">true</parameter>
<parameter name="mail.smtp.auth">true</parameter>
</transportSender>

Déclaration d’un Identity Provider Email OTP

Il vous faudra ensuite déclarer un Identity Provider avec le Federated Authenticator Email OTP d’activé.

Déploiement d’une application de test

Nous utiliserons pour nos tests l’application de test SAMLToolkit https://www.samltool.com/toolkits.php

Elle nécessite l’installation d’un serveur Tomcat et le déploiement sur celui d’un jar de test java-saml-tookit-jspsample.war

Pour configurer le client saml2 vous pouvez éditer le fichier java-saml-tookit-java-saml-tookit-jspsample.war/WEB-INF/classes/onelogin.saml.properties

# Identity Provider Data that we want connect with our SP
#
# Identifier of the IdP entity (must be a URI)
onelogin.saml2.idp.entityid = localhost
# SSO endpoint info of the IdP. (Authentication Request protocol)
# URL Target of the IdP where the SP will send the Authentication Request Message
onelogin.saml2.idp.single_sign_on_service.url = https://localhost:9443/samlsso
# SAML protocol binding to be used when returning the <Response>
# message. Onelogin Toolkit supports for this endpoint the
# HTTP-Redirect binding only
onelogin.saml2.idp.single_sign_on_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect
# SLO endpoint info of the IdP.
# URL Location of the IdP where the SP will send the SLO Request
onelogin.saml2.idp.single_logout_service.url = https://localhost:9443/samlsso
# Optional SLO Response endpoint info of the IdP.
# URL Location of the IdP where the SP will send the SLO Response. If left blank, same URL as onelogin.saml2.idp.single_logout_service.url will be used.
# Some IdPs use a separate URL for sending a logout request and response, use this property to set the separate response url
onelogin.saml2.idp.single_logout_service.response.url = https://localhost:9443/samlsso
# SAML protocol binding to be used when returning the <Response>
# message. Onelogin Toolkit supports for this endpoint the
# HTTP-Redirect binding only
onelogin.saml2.idp.single_logout_service.binding = urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect
# Public x509 certificate of the IdP
onelogin.saml2.idp.x509cert = -----BEGIN CERTIFICATE-----MIIDSTCCAjGgAwIBAgIEAoLQ/TANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxDTALBgNVBAoTBFdTTzIxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xNzA3MTkwNjUyNTFaFw0yNzA3MTcwNjUyNTFaMFUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzENMAsGA1UEChMEV1NPMjESMBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAluZFdW1ynitztkWLC6xKegbRWxky+5P0p4ShYEOkHs30QI2VCuR6Qo4Bz5rTgLBrky03W1GAVrZxuvKRGj9V9+PmjdGtau4CTXu9pLLcqnruaczoSdvBYA3lS9a7zgFU0+s6kMl2EhB+rk7gXluEep7lIOenzfl2f6IoTKa2fVgVd3YKiSGsyL4tztS70vmmX121qm0sTJdKWP4HxXyqK9neolXI9fYyHOYILVNZ69z/73OOVhkh/mvTmWZLM7GM6sApmyLX6OXUp8z0pkY+vT/9+zRxxQs7GurC4/C1nK3rI/0ySUgGEafO1atNjYmlFN+M3tZX6nEcA6g94IavyQIDAQABoyEwHzAdBgNVHQ4EFgQUtS8kIYxQ8UVvVrZSdgyide9OHxUwDQYJKoZIhvcNAQELBQADggEBABfk5mqsVUrpFCYTZZhOxTRRpGXqoW1G05bOxHxs42Paxw8rAJ06Pty9jqM1CgRPpqvZa2lPQBQqZrHkdDE06q4NG0DqMH8NT+tNkXBe9YTre3EJCSfsvswtLVDZ7GDvTHKojJjQvdVCzRj6XH5Truwefb4BJz9APtnlyJIvjHk1hdozqyOniVZd0QOxLAbcdt946chNdQvCm6aUOputp8Xogr0KBnEy3U8es2cAfNZaEkPU8Va5bU6Xjny8zGQnXCXxPKp7sMpgO93nPBt/liX1qfyXM7xEotWoxmm6HZx8oWQ8U5aiXjZ5RKDWCCq4ZuXl6wVsUz1iE61suO5yWi8=-----END CERTIFICATE-----

Vous pouvez configurer la classe de contexte attendue à ce niveau :

# Authentication context.
# Set Empty and no AuthContext will be sent in the AuthNRequest
# You can set multiple values (comma separated them)
onelogin.saml2.security.requested_authncontext = urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport

Déclaration du Service Provider de test

Il nous faut déclarer notre Service Provider classiquement et pour vous aider l’application de test génère un fichier de métadata SAML 2.0 qu’il vous suffit d’importer. Il est présent à l’url http://localhost:8080/java-saml-tookit-jspsample/metadata.jsp

Attention la configuration par défaut de l’application de test nécessite absolument la signature de la réponse mais celle-ci n’est pas demandée dans le fichier de metadata:

#  If 'strict' is True, then the Java Toolkit will reject unsigned
# or unencrypted messages if it expects them signed or encrypted
# Also will reject the messages if not strictly follow the SAML
onelogin.saml2.strict = false

Configuration de l’authentification adaptive

Une fois la connexion à votre service provider de déclarer il vous faut configurer une authentification avancée avec un step pour chaque type d’authentification souhaitée:

il ne vous reste plus qu’à mettre en oeuvre le template ACR de l’authentification adaptive pour orchestrer vos 2 steps:

Et voilà!

--

--

Gregory EVE
Smile with WSO2

Solution Architect at Smile, french lover and open source supporter. Integrate, Search, Leverage and Secure your data what else?