Au cours de mes différentes expérimentations, j’ai voulu renforcer l’authentification de certains de mes services en m’appuyant sur une infrastructure à clés publiques dont je disposais déjà. Étant donné que la configuration du VirtualHost sur Apache n’a pas forcément été simple à mettre en place, je vous propose par la suite un template de Virtualhost que j’ai conçu. Les aspects relatifs à la gestion d’une PKI ou de la génération de certificats ne sera pas traitée dans ce billet.

Prérequis

La présente configuration suppose que vous disposiez au préalable:

  • d’un serveur Apache fonctionnel avec un virtualhost qui vous permettra de accueillir les utilisateurs dont l’authentification a échoué
  • des modules apache suivants (liste non exhaustive): mod_ssl, mod_rewrite, mod_headers
  • de certificats SSL serveurs et clients valides.

Gestion des erreurs

Un des problèmes principaux de l’authentification SSL repose sur le fait que le client se retrouve sur une page d’erreur du navigateur et non sur une page d’erreur de votre site web. La configuration suivante vous permettra de rediriger le client vers une autre page lui proposant éventuellement d’autres modes d’authentification (forte de préférence). La redirection mise en place est de type HTTP/307 et est complètement standard.

Pour notre exemple, je vous fournis une mire d’échec très simpliste en PHP qui permettra au client de relancer la procédure d’authentification vers le bon site une fois les problèmes corrigés de son côté. Pour la suite de ce billet, on supposera cette page accessible via l’URL suivante: https://login.yourdomain.com/sslfail.php

<html>
    <head>
        <title>FAIL</title>
    </head>
    <body>
        <p>SSL authentication failed. <?php
            if(isset($_GET["from"]))
            {
                echo("<a href=\"https://". $_GET["from"] .".yourdomain.com/\">Try Again.</a>");
            }
        ?></p>
    </body>
</html>

Le fichier VirtualHost

Pour la suite, on va considérer que le service à protéger sera accessible aux clients sur secureservice.yourdomain.com.

<IfModule mod_ssl.c>
    <VirtualHost *:443>
        ServerName secureservice.yourdomain.com
        DocumentRoot /var/secureservice-wwws

        # Configuration SSL côté serveur
        SSLEngine on
        SSLCertificateFile    /path/to/ca/certs/secureservice.pem
        SSLCertificateKeyFile /path/to/ca/private/secureservice.key
        SSLCertificateChainFile /path/to/ca/keystore/ca.crt

        # Configuration SSL côté client
        SSLCACertificateFile /path/to/ca/certs/ca.pem
        SSLCARevocationFile /path/to/ca/crl/ca-crl.crl
        SSLVerifyDepth  10

        <IfModule mod_rewrite.c>
            # If mod_rewrite is available, redirect clients on failure
            SSLVerifyClient optional

            <IfModule mod_headers.c>
                Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
                Header set Pragma "no-cache"
                Header set Expires "Wed, 11 Jan 1984 05:00:00 GMT"
            </IfModule>
            RewriteEngine On
            RewriteCond %{SSL:SSL_CLIENT_VERIFY} !^SUCCESS$
            RewriteRule .* https://login.yourdomain.com/sslfail.php?from=secureservice [r=307]
        </IfModule>
        <IfModule !mod_rewrite.c>
            # If mod_rewrite is not available, just deny clients on failure
            SSLVerifyClient require
        </IfModule>

        BrowserMatch "MSIE [2-6]" \
            nokeepalive ssl-unclean-shutdown \
            downgrade-1.0 force-response-1.0
        # MSIE 7 and newer should be able to use keepalive
        BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
    </VirtualHost>
</IfModule>

Concernant les attributs côté client, je tiens à vous préciser quelque points: en plus de la configuration classique du certificat serveur, 3 lignes sont impératives pour mettre en place l’authentification SSL:

  • La ligne qui dit au serveur où chercher les certificats des CA qui ont signé les certificats client (SSLCACertificateFile). Le chemin doit impérativement pointer sur un fichier encodé au format PEM. Si vous avez plusieurs CA à prendre en compte, concaténez vos différents fichiers PEM et faites pointer cette ligne sur le fichier résultant.
  • La ligne qui indique combien de niveaux de validation sont supportés pour les certificats clients (SSLVerifyDepth).  Si la valeur associée vaut zéro, le certificat client doit être auto-signé. Si la valeur vaut 1, le certificat doit être validé par le certificat d’une autorité présente dans le fichier des autorité client. Si la valeur vaut 2 ou plus, le certificat peut avoir été signé par une autorité intermédiaire valide. Avec 2, le client peut avoir été signé par une CA signée par une CA présente dans le fichier des autorités.
  • La ligne qui va spécifier le comportement à tenir en cas d’échec. Lorsque SSLVerifyClient est positionné sur require, la négociation SSL/TLS sera annulée si le certificat de l’utilisateur n’est pas validé par le serveur. Le client sera informé par son navigateur que l’opération a échoué. Si par contre SSLVerifyClient est positionné sur optional, et que le certificat de l’utilisateur n’est toujours pas valide, la connexion s’établit quand même, mais il sera, dans notre cas, redirigé vers la page d’échec (http://login.tourdomain.com/sslfail.php?from=secureservice) par le biais d’une réponse HTTP/307. À ce sujet, il est très important d’ajouter le module headers à apache, car les navigateurs ont tendance à mettre en cache les redirections. Si une redirection est mise en cache, votre client pourrait croire que son authentification échoue en permanence, ce qui n’est pas souhaitable.

La spécification d’une liste de révocation de certificats n’est pas toujours nécessaire, mais il reste préférable de l’utiliser car elle vous permettra d’assurer une gestion des droits d’accès plus propre. La ligne avec SSLCARevocationFile doit pointer sur un fichier qui contient la concaténation de toutes les listes de révocation des autorités client encodées au format PEM.