Page en cours de relecture

7. Shibbolisation simple d'une application web (PHP)

7.1. Installation de myBlog

7.1.1. Installation et configuration

Afin d'adapter le contenu de cette page avec le nom de votre serveur, saisissez-le ici :

Nom de votre machine :


Téléchargez l'application :

$ cd /var/www/cgi-bin
$ git clone git+ssh://git@git.renater.fr:2222/partage-fede/myblog.git
$ chown -R apache:apache myblog

Vous devez ensuite aménager un point d'accès à l'application, dans le fichier de configuration de Apache au niveau d'un ScriptAlias déjà existant :

/etc/httpd/conf.d/myblog.conf
**ScriptAlias /myBlog /var/www/cgi-bin/myblog/php/index.php**

Redémarrez ensuite votre serveur Apache pour prendre en compte la modification :

$ systemctl restart httpd


Vous pouvez tester que l'application répond en accédant avec votre navigateur à https://monposte.fr/myBlog

7.1.2. Fonctionnement de l'application

L'application myBlog permet de gérer des billets. La liste des billets est accessible publiquement, alors que la création d'un billet requiert une authentification de l'utilisateur. Le droit de supprimer un billet est réservé à l'auteur de celui-ci ou à un administrateur. Les mots de passe des utilisateurs ainsi que leur profil (admin ou user) sont définis dans le fichier myblog/php/users :

/var/www/cgi-bin/myblog/php/users
user1:pwd1:admin
user2:pwd2:user

Le codePHP rendant le service est myblog/php/index.php. La session d'authentification avec l'utilisateur est maintenue via le cookie HTTP intitulé BLOG_USER.

Les bibliothèques myblog/php/Blog.class.php et myblog/php/Billet.class.php définissent les fonctions métier.

Les pages web sont définies dans un unique modèle de page myblog/php/templates/index.tpl.php.

Vous pouvez tester l'authentification native de l'application, l'écriture et le listage de billets avec les logins et mot de passe définis dans myblog/php/users.

7.2. Intégration minimale avec le SP Shibboleth

L'intégration minimale consiste à activer une authentification Shibboleth systématique en amont de l'application. L'utilisateur devra donc systématiquement s'authentifier pour accéder à l'application. La brique SP Shibboleth oriente l'utilisateur vers son fournisseur d'identités (utilisation d'un IdP de test par défaut). Une fois l'utilisateur authentifié, il est redirigé vers l'application myBlog ; l'application reçoit les attributs de l'utilisateur via des variables d'environnement HTTP. Il faudra donc modifier l'application pour exploiter l'identifiant de l'utilisateur ainsi reçu via Shibboleth et ne plus proposer la fonction d'authentification native.

Les attributs multi-valués sont regroupés dans une seule variable, chaque valeur étant séparée par le caractère « ; ». Si un attribut contient lui-même le caractère « ; », ce caractère est échappé sous la forme « \; ».

7.2.1. Configuration de Apache

Nous allons éditer la configuration Apache pour activer l'authentification Shibboleth lors de l'accès à l'URL /myBlog.

/etc/httpd/conf.d/myblog.conf
...
**<Location /myBlog>**
  **AuthType shibboleth**
  **ShibRequestSetting requireSession 1**
  **require shib-session**
**</Location>**


$ systemctl reload httpd

On modifie à présent le fichier myblog/php/users afin d'ajouter l'utilisateur dont l'identifiant (EPPN) est etudiant1@univ-test.fr pour lui assigner un rôle user :

/var/www/cgi-bin/myblog/php/users
user1:pwd1:admin
user2:pwd2:user
**etudiant1@univ-test.fr:null:user**

Recueil de l'attribut EPPN : pour attribuer des droits à un utilisateur associés à son EPPN, il est nécessaire de connaitre cet EPPN. Or l'utilisateur n'est pas censé connaitre la valeur de son propre EPPN. Il faut donc mettre en oeuvre un mécanisme de recueil d'EPPN, sous la forme d'un processus d'invitation.

Exemple de workflow d'invitation en vue de recueillir des EPPN :

  • l'administrateur de l'application saisit l'adresse email de l'utilisateur à qui il souhaite octroyer des droits applicatifs
  • l'application génère une clé de validation associée à cet utilisateur et envoie à l'utilisateur un mail d'invitation contenant une URL de validation incluant la clé de validation
  • l'utilisateur reçoit le mail d'invitation et accède à l'URL de validation qui nécessite une authentification préalable. L'application reçoit l'attribut eduPersonPrincipalName (EPPN) en provenance de l'IdP de l'utilisateur et peut lui octroyer les droits applicatifs
  • lors de sa prochaine connexion à l'application l'utilisateur est reconnu, après authentification, sur la base de son EPPN

Autre workflow possible :

  • Un utilisateur tente d'accéder. Suite à la connexion à son IdP, il est redirigé vers l'application
  • Si l'application ne connaît pas l'EPPN, elle peut créer l'utilisateur, en se basant sur d'autres attributs présents pour lui affecter un rôle

Si vous accédez à nouveau à l'application https://monposte.fr/myBlog vous passerez par une authentification Shibboleth. Dans le cas présent, vous ne le remarquerez pas forcément car le SP vous a déjà authentifié lors de l'accès à https://monposte.fr/secure. Pour dérouler entièrement la séquence d'authentification, ppassez en navigation privée ou effacez tous les cookies de votre navigateur puis accédez à nouveau à l'application.

Pour l'instant l'application n'est pas capable d'exploiter les attributs qui lui sont transmis par le SP Shibboleth : vous le constatez en voyant que vous n'êtes pas connecté en arrivant sur https://monposte.fr/myBlog après la séquence Shibboleth. En effet, l'application n'a pas encore été adaptée pour exploiter les attributs remontés par Shibboleth.

7.2.2. Modification de l'application pour exploiter les attributs utilisateur

Quand un utilisateur est effectivement authentifié sur un IdP via Shibboleth, le module SP reçoit en retour une assertion SAML contenant en général un identifiant de l'utilisateur et d'autres attributs. Le module SP analyse cette assertion et en extrait les différents attributs pour renseigner des en-têtes HTTP. Cette mise en correspondance entre les attributs de l'assertion SAML et les en-têtes HTTP est définie dans le fichier /etc/shibboleth/attribute-map.xml. Par exemple, la valeur de l'attribut urn:mace:dir:attribute-def:displayName est positionnée dans l'en-tête displayName.

L'en-tête REMOTE_USER fait exception, il est lui défini via /etc/shibboleth/shibboleth2.xml :

/etc/shibboleth/shibboleth2.xml
<SPConfig ...>
...
  <ApplicationDefaults entityID="https://**monposte.fr**"
    **REMOTE_USER="eppn persistent-id targeted-id"**
    cipherSuites="HIGH:!MD5:!RC4:!aNULL">
...

Cette règle indique au SP de renseigner cet en-tête d'abord avec l'en-tête mail (issu de l'attribut urn:mace:dir:attribute-def:mail fourni par l'IdP) et s'il n'existe pas avec l'en-tête persistent-id et s'il n'existe pas non plus avec targeded-id.

Tous ces en-têtes HTTP sont automatiquement mis à disposition des applications par le serveur Apache sous forme de variables d'environnement (pour les applications php /CGI, PHP…). Pour qu'une application accède aux attributs remontés par Shibboleth, il suffit donc que son code lise ces variables d'environnement.
Dans le cas d'une application protégée par Shibboleth SP mais non exécutée par Apache (Java, Ou application proxyfiée), les informations sont fournies via ces même en-tête HTTP.

Exploitons ce mécanisme dans myBlog pour authentifier l'utilisateur via Shibboleth : il suffit à l'application de vérifier si la variable d'environnement REMOTE_USER a une valeur.

7.2.3. Modification de l'application

/var/www/cgi-bin/myblog/php/Blog.class.php
  ...
    **if(getenv('eppn') != '' && getenv('REMOTE_USER') != '') {**
        **$this->utilisateur = getenv('eppn');**
        **$users = self::chargeUsers();**
        **$this->role_utilisateur = $users[$this->utilisateur]['role'];**
  **}** else if(isset($_COOKIE['BLOG_USER']) && !empty($_COOKIE['BLOG_USER'])) {
  				$this->utilisateur = $_COOKIE['BLOG_USER'];
  				$users = self::chargeUsers();
  				$this->role_utilisateur = $users[$this->utilisateur]['role'];
    }

Cette modification du code permet d'initialiser une session utilisateur si la variable d'environnement eppn est positionnée.

On peut également exploiter le nom de l'utilisateur (displayName) pour l'afficher dans l'application à la place de l'identifiant :

/var/www/cgi-bin/myblog/php/Blog.class.php
  var $utilisateur = '';
  **var $nom_utilisateur = '';**
  var $role_utilisateur = '';
...
if(getenv('eppn') != '' && getenv('REMOTE_USER') != '') {
    $this->utilisateur = getenv('eppn');
    **$this->nom_utilisateur = getenv('displayName');**
    $users = self::chargeUsers();
    $this->role_utilisateur = $users[$this->utilisateur]['role'];
/var/www/cgi-bin/myblog/php/templates/index.tpl.php
**<?php if($this->utilisateur != '') echo 'Logged in as <strong>'.(($this->nom_utilisateur != '') ? $this->nom_utilisateur : $this->utilisateur).'</strong><br />'; ?>**
 
 
<?php if($this->role_utilisateur != '') echo 'Role : '.$this->role_utilisateur.'<br />'; ?>

7.2.4. Tester

Connectez-vous sur https://monposte.fr/myBlog pour vérifier qu'après la séquence d'authentification via Shibboleth vous arrivez connecté avec l'identité de votre IdP de Test.

Ne vous connectez pas auprès de l'IdP Comptes CRU qui ne transmet pas d'attribut nominatif par défaut. Vous pouvez utiliser l'IdP que vous avez installé.

Conseil aux développeurs : dans notre code d'exemple le nom de la variable d'environnement contenant les attributs utilisateurs (eppn, mail et displayName) est codé en dur. Si vous adaptez une application en production, rendez ces variables paramétrables afin de vous adapter à des évolutions de Shibboleth ou l'utilisation d'autres logiciels de d'authentification.

7.3. Implémentation de la déconnexion

Vous remarquerez que la fonction de déconnexion de l'application ne rend plus son office puisque lorsque vous cliquez sur le lien logout, vous êtes effectivement déconnecté, mais si vous vous rendez de nouveau sur https://monposte.fr/myBlog vous vous retrouvez à nouveau connecté. En effet, la déconnexion n'a pas été propagée depuis l'application jusqu'au SP Shibboleth. Ce dernier maintient toujours une session pour l'utilisateur, et continue donc à transmettre à chaque requête du navigateur à l'application les variables d'environnement où l'identité de l'utilisateur est positionnée : pour l'application c'est comme si l'utilisateur était à nouveau connecté via Shibboleth.

La session utilisateur au niveau du SP Shibboleth peut être terminée en redirigeant l'utilisateur vers une URL de la forme https://monposte.fr/Shibboleth.sso/Logout. Cependant cette déconnexion ne s'applique que localement, elle n'est propagée ni au fournisseur d'identités (et au serveur CAS associé), ni aux autres applications auprès desquelles l'utilisateur est éventuellement connecté.

Nous allons donc mettre en œuvre la déconnexion Shibboleth locale. Pour cela nous allons modifier la fonction req_logout() de notre application pour orienter l'utilisateur vers cette URL de logout Shibboleth, une fois le logout applicatif effectué :

7.3.1. Modification de l'application

/var/www/cgi-bin/myBlog/Blog.class.php
...
function req_logout() {
	$this->utilisateur = '';
	$this->role_utilisateur = '';
        ** $this->url_redirection = 'https://monposte.fr/Shibboleth.sso/Logout?return=https://monposte.fr/myBlog';**
 
        **// LIGNE A RETIRER $this->next_action = 'index';**
}

7.3.2. Tester

Rechargez la page https://monposte.fr/myBlog pour prendre en compte cette modification, puis cliquez sur le lien logout pour tester cette déconnexion locale. Vous remarquerez que, à la suite du logout, vous êtes à nouveau orienté vers la séquence d'authentification Shibboleth. En effet, nous avons activé une authentification systématique en amont du CGI ; nous allons maintenant essayer de faire mieux.

7.4. Utilisation des « lazy sessions » pour permettre une navigation anonyme

Le mécanisme des lazy sessions est propre à Shibboleth. Ce mécanisme permet de déclencher la phase d'authentification de l'utilisateur, non plus à l'initiative de la directive ShibRequestSetting requireSession 1 définie dans Apache en amont de l'application, mais depuis l'application : c'est l'application qui déclenche, quand elle le souhaite, la séquence Shibboleth. Pour ce faire, l'application redirige le navigateur de l'utilisateur vers une URL spécifique pour le SP Shibboleth : c'est seulement quand il est contacté via cette URL spécifique que l'authentification sur le SP Shibboleth est déclenchée.

Nous allons modifier notre blog pour déclencher l'authentification Shibboleth lorsque l'utilisateur cliquera sur un nouveau lien login Shibboleth. L'opération déclenchée par ce clic sera l'envoi d'un en-tête HTTP de redirection (Location: url) afin d'envoyer le navigateur de l'utilisateur vers l'URL spécifique d'activation du SP Shibboleth.

7.4.1. Désactiver l'authentification systématique

Dans la configuration du serveur Apache, nous allons modifier la directive commandant le déclenchement de l'authentification Shibboleth :

/etc/httpd/conf.d/shib.conf
<Location /myBlog>
  AuthType shibboleth
**  ShibRequestSetting requireSession 0**
**  require shibboleth**
</Location>

ShibRequestSetting requireSession 0 empêche Shibboleth de se déclencher automatiquement. La directive require shibboleth est nécessaire dans le contexte des lazy sessions pour permettre à Apache de faire appel au module mod_shib sans définir de règle de contrôle d'accès spécifique (cf https://wiki.shibboleth.net/confluence/display/SP3/htaccess).

Relancez Apache pour prendre en compte ces modifications :

$ systemctl restart httpd

7.4.2. Construction de l'URL d'activation des lazy sessions

Documentation de référence : https://wiki.shibboleth.net/confluence/display/SP3/SessionInitiator

Le mécanisme de lazy sessions est déclenché lorsque l'utilisateur est redirigé vers une URL prévue à cette effet. Cette URL, utilisée dans votre application, n'est pas directement présente dans le fichier de configuration de Shibboleth ; vous devez construire cette URL à partir de plusieurs éléments définis dans votre fichier /etc/shibboleth2.xml, de la façon suivante :

handler_url + ref_session_initiator + paramètres

Dans notre cas de figure, cette URL est la suivante :

https://monposte.fr/Shibboleth.sso/Login?target=https://monposte.fr/myBlog

Le paramètre target permet de spécifier l'adresse de retour, une fois l'utilisateur authentifié.

Si l'URL spécifiée par le paramètre target comprend des caractères gênants dans une URL, vous devrez prendre soin de les échapper préalablement.

7.4.3. Mise à jour de l'application

Nous allons ajouter un nouveau lien de login à l'interface graphique de l'application ; l'utilisateur pourra ainsi s'authentifier avec la méthode pré-existante ou avec Shibboleth.

/var/www/cgi-bin/myblog/php/templates/index.tpl.php
else echo '<a href="?action=login">login</a> **| <a href="?action=shiblogin">login avec Shibboleth</a>'**; ?>

Nous allons créer une nouvelle fonction req_shiblogin() qui sera associée à l'action shiblogin de notre blog :

/var/www/cgi-bin/myblog/php/Blog.class.php
...
	}else if($this->action == 'login') {
		$this->req_login();
	**}else if($this->action == 'shiblogin') {**
	**	$this->req_shiblogin();**
	}else if($this->action == 'logout') {
		$this->req_logout();
	}else if($this->action == 'effacer') {
		$this->req_effacer();
	}
...
**## Shibboleth Login**
**function req_shiblogin() {**
**	$this->url_redirection = 'https://monposte.fr/Shibboleth.sso/Login?target=https://monposte.fr/myBlog';**
**}**
 
## Logout
function req_logout() {

7.4.4. Tester

Vous pouvez maintenant tester la nouvelle fonction de login Shibboleth en accédant à https://monposte.fr/myBlog puis en cliquant sur « Login avec Shibboleth ».

7.5. Utilisation de l'embedded WAYF

Documentation de référence: http://www.switch.ch/aai/support/serviceproviders/sp-embedded-wayf.html

Le WAYF de SWITCH implémente une fonctionnalité originale qui permet à une application d'inclure le code javascript permettant de générer le menu déroulant du WAYF.

Nous allons adapter le modèle de page HTML du moteur de blog pour y ajouter le « embedded WAYF ».

7.5.1. Personnalisation du modèle de page

Nous allons ajouter le code javascript incluant le WAYF dans notre modèle de page.

Éditez /var/www/cgi-bin/myblog/php/templates/index.tpl.php
code à rajouter après la balise : <body>

/var/www/cgi-bin/myblog/php/templates/index.tpl.php
...
<!-- EMBEDDED-WAYF-START -->
<script type="text/javascript"><!--
//////////////////// ESSENTIAL SETTINGS ////////////////////
 
// EntityID of the Service Provider that protects this Resource
// [Mandatory]
wayf_sp_entityID = "https://monposte.fr";
 
// Shibboleth Service Provider handler URL
// Examples: "https://point.switch.ch/Shibboleth.sso", "https://rr.aai.switch.ch/aaitest/Shibboleth.sso"
// [Mandatory, if wayf_use_discovery_service = false]
var wayf_sp_handlerURL = "https://monposte.fr/Shibboleth.sso";
 
// Session Initiator URL of the Service Provider
var wayf_sp_samlDSURL = wayf_sp_handlerURL + "/Login";
 
// URL of the WAYF to use
// [Mandatory]
wayf_URL = "https://monposte.fr/wayf/WAYF";
 
// URL on this resource that the user shall be returned to after authentication
// [Mandatory]
wayf_return_url = "https://monposte.fr/myBlog";
 
// Whether to show the checkbox to remember settings for this session
var wayf_show_remember_checkbox = false;
 
// Width of the embedded WAYF in pixels or "auto"
var wayf_width = 205;
 
// Overwrites the text of the submit button
var wayf_overwrite_submit_button_text = 'Go';
 
// Overwrites the intro text above the drop-down list
var wayf_overwrite_intro_text = 'Login shibboleth';
 
// Whether to hide the WAYF after the user was logged in
// If you want to hide the embedded WAYF completely, uncomment
// the property and set it to "". This then won't draw anything
var wayf_logged_in_messsage = "Vous êtes authentifié";
 
// If enabled, the Embedded WAYF will activate the
// improved drop down list feature, which will transform the list of
// organisations into a search-field while keeping its original function as
// a select list. To make this work, the JQuery library will dynamically be
// loaded if it is not yet present. Additionally, another Javascript and CSS
// file are loaded to perform the actual transformation.
// Please note that this feature will also display the organisations' logos,
// which might be loaded from a remote domain. While generally not especially
// dangerous, there is always a risk when loading content (in this case
// images) from third party hosts.
// [Optional, default: false]
var wayf_use_improved_drop_down_list = true;
 
//-->
</script>
 
<script type="text/javascript" src="https://monposte.fr/wayf/WAYF/embedded-wayf.js"></script>
 
<noscript>
  <!--
  Fallback to Shibboleth DS session initiator for non-JavaScript users
  You should set the value of the target GET parameter to an URL-encoded
  absolute URL that points to a Shibboleth protected web page where the user
  is logged in into your application.
  -->
  <p>
    <strong>Login:</strong> Javascript is not available for your web browser. Therefore, please <a href="/Shibboleth.sso/Login?target=">proceed manually</a>.
  </p>
</noscript>
 
<!-- EMBEDDED-WAYF-END -->
 
<div class="footer">Powered by myBlog</div>

Vous pouvez tester le nouveau WAYF en accédant à l'application myBlog en navigation privée ou après avoir supprimé tous vos cookies.

Exemple de code à inclure : le WAYF retourne un exemple de code à inclure à l'URL suivante https://monposte.fr/wayf/WAYF/embedded-wayf.js/snippet.html. Cela vous fournit un exemple présentant les possibilités de configuration.
Pour un exemple de code complet (avec toutes les options) du WAYF RENATER, accédez l'URL https://discovery.renater.fr/renater/WAYF/embedded-wayf.js/snippet.html

7.5.2. Intérêts de cette solution

Cette solution offre à la fois un bon niveau d'intégration (puisque le menu déroulant du WAYF est directement dans l'application) et est peu intrusive dans l'application.

Les intérêts de cette solution pour le mainteneur de l'application :

Les intérêts pour les utilisateurs :

  • Rendu utilisateur homogène (pas de redirection),
  • Cookie de pré-sélection partagé avec le WAYF.