Avoir une bonne configuration SSL avec nginx
Aujourd'hui, j'ai découvert le testeur de configuration SSL de Quarlys, qui vérifie automatiquement si un serveur donné est vulnérable à des failles classiques, ou a des problèmes de configuration. C'est intéressant de passer le site de sa banque, ou d'un autre site supposé sérieux et critique, au travers de cette moulinette — ma banque suisse obtient un B ; ma banque française un gros F bien rouge et bien déprimant.
Évidemment, vu que j'utilise SSL sur ce site (vous pouvez vérifier que https://desfontain.es fonctionne bien), j'ai voulu savoir la note que j'obtenais à ce test. Deux minutes plus tard, j'ai pu lire « C: This server is vulnerable to the POODLE attack. If possible, disable SSL 3 to mitigate. Grade capped to C. ».
Fichtre. Allons donc joyeusement désactiver SSL 3, puisqu'il est troué, et
n'autorisons que TLS. Ça se fait de la façon suivante. Il faut ouvrir le fichier
contenant les règles de nginx (/etc/nginx/sites-enabled/default
dans mon cas),
et y ajouter une ligne entre la paire d'accolades commençant par server {
,
après les lignes qui parlent déjà de SSL (listen 443 default ssl
& cie):
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
On redémarre nginx (en root, bien sûr):
service nginx restart
on repasse le test, et ce coup-ci, « A-: The server does not support Forward Secrecy with the reference browsers. Grade capped to A-. ». Hmmm. A-, c'est pas mal, mais c'est quand même significativement moins bien que A.
Surtout que la Forward Secrecy, c'est un concept plutôt cool qui permet d'avoir la garantie de sécurité suivante : si un attaquant récupère l'échange chiffré entre le client et le serveur ; et que plus tard il obtient la clé privée du serveur, il ne peut pas déchiffrer l'échange passé. C'est magique. Je ne sais pas comment ça marche. Mais ça crache du feu, et le fait que ça ne marche pas avec tous les navigateurs possibles est perturbant.
La raison est décrite par ce billet (en anglais) : les paramètres que nginx choisit par défaut pour le protocole sont ceux d'OpenSSL. Or, OpenSSL utilise par défaut une clé de 1024 bits ; tandis que le certificat qu'on utilise fait 2048 bits. Du coup, c'est pas compatible, et les clients vont utiliser un protocole moins cool qui n'a pas la propriété qu'on veut. On va donc générer des meilleurs paramètres à la main :
cd /etc/ssl/certs
openssl dhparam -out dhparam.pem 4096
et dire à nginx de les utiliser en ajoutant ça au même endroit que tout à l'heure:
ssl_dhparam /etc/ssl/certs/dhparam.pem;
et pouf, Forward Secrecy, ça c'est fait. Re-testons allègrement notre serveur après redémarrage de nginx… Ooooh, un joli « A » ! Joie, bonheur et allégresse.
Le billet que j'ai mis en lien un petit peu au-dessus a quelques sections supplémentaires. Par exemple, le petit bout suivant :
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.4.4 8.8.8.8 valid=300s;
resolver_timeout 10s;
active le « OCSP Stapling », une technologie permettant de mettre en cache dans le serveur la réponse de l'autorité du certification quand le client demande une preuve d'identification. C'est un peu comme si on était dans un pays étranger où à chaque fois qu'on rencontre un policier, il demande qu'on lui montre le tampon du pays sur notre passeport. Comme y'a beaucoup de policiers, au bout d'un moment, on en a un peu marre, surtout que le document est au fin fond de notre sac et que du coup, ça nous fait perdre du temps à chaque fois. La solution naturelle, c'est de s'agrafer une photocopie du tampon sur le manteau, comme ça le policier peut juste jeter un coup d'œil et être convaincu, et on perd moins de temps.
(Je sais. Cette analogie n'est pas parfaite. Mais elle est absurde, donc vous allez la retenir. Et de toute façon, vous avez probablement compris que vous pouviez tout aussi bien copier-coller tous les bouts de configuration de cet article dans votre configuration nginx sans vous poser tellement de questions, donc vous allez arrêter de m'embêter.)
Enfin, on peut ajouter quelques chouettes headers HTTP aux réponses qu'on envoie aux clients, pour améliorer encore notre configuration. La ligne suivante :
add_header Strict-Transport-Security max-age=63072000;
active le « HSTS » (HTTP Strict Transport Security), ce qui dit aux navigateurs qui vont sur notre site en utilisant la version HTTP non sécurisée par défaut « hey, y'a une version sécurisée, utilisez-la :D », et si ils ne sont pas trop stupides (erm Internet Explorer 6 erm), ils le font.
Remarque (merci à elarnon) : un inconvénient de HSTS, c'est que si jamais HTTPS ne marche pas pour une raison ou pour une autre (on a oublié de renouveler son certificat, le serveur OCSP ne répond plus, quelqu'un est en train d'essayer de faire un man-in-the-middle…), la version HTTP du site ne sera plus disponible. Ça n'est donc pas très adapté aux sites dont la disponibilité est capitale, mais qui ne gèrent pas de données critiques qui ne supporteraient pas d'être envoyés en clair. Bon, en vrai, si on fait attention à ne pas que son certificat expire, ça ne devrait pas poser de problème majeur.
Ce header-ci :
add_header X-Frame-Options DENY;
permet d'interdire que le contenu de mon site soit réutilisé tel quel dans une
balise <frame>
, <iframe>
ou <object>
, ce qui permet de se protéger contre
les attaques par détournement de
clic. Je… ne sais pas
dans quel contexte une telle attaque pourrait avoir du sens sur mon brave petit
site perso. Mais ça ne coûte pas très cher. Même remarque à propos de ce dernier
header :
add_header X-Content-Type-Options nosniff;
qui permet d'éviter que des navigateurs essaient de deviner le type d'un fichier qu'on leur envoie en regardant son contenu, plutôt que le type de ses headers. Pourquoi ça a du sens de faire ça avec mon contenu, je n'en ai pas la moindre espèce d'idée, parce que (pour autant que je sache) je documente bien le type de mes fichiers avant de les envoyer sur mon serveur, mais oh well.
Je vous ai probablement perdu. Ou vous lisez ces lignes après avoir parcouru les deux tiers de cet article sans le lire. Je ne vous en veux pas. Passez une bonne journée ! =D