L’ami Angristan a posté un excellent billet il y a quelques semaines consacré à la configuration HTTPS sur NGinx. Son billet a fini de me convaincre de rédiger un petit mémo expliquant comment mieux sécuriser HTTPS puisque, comme il le dit si bien, « HTTPS ne se résume pas avoir avoir le cadenas vert dans votre navigateur! ». J’ai donc décidé de pousser le vice un peu plus loin
Sécuriser HTTPS : Mise en place d’HTTPS
Redirection HTTP vers HTTPS
Tout d’abord, je vous conseille de bien formater votre configuration NGinx, il est ensuite beaucoup plus facile de s’y retrouver et donc de corriger les potentielles erreurs.
Ensuite je mets en place une redirection pour forcer le client à se connecter en HTTPS
#------------------------------------------------------------------------ # REDIRECTION HTTPS # server { listen 80 default_server; listen [::]:80 default_server; server_name www.votre-domaine.tld; return 301 https://www.votre-domaine.tld$request_uri; }
Configuration block serveur HTTPS
Maintenant voici à quoi ressemble une configuration HTTPS basique en fonction de la version de NGinx installée sur votre serveur.
Pour une configuration basique du protocole HTTPS vous avez seulement besoin de demander à NGinx d’écouter sur le port 443 et de définir les variables ssl_certificate
et ssl_certificate_key
.
Block serveur HTTPS – NGINX < 1.9.5
Si votre version de NGinx est supérieure à la 1.5.10 5 (je l’espère de tout cœur) mais inférieure à la 1.9.5 utilisez le block server ci-dessous.
#------------------------------------------------------------------------ # BLOCK SERVEUR HTTPS # server { listen 443 ssl spdy; server_name www.votre-domaine.tld; root /var/www/www.votre-domaine.tld; index index.php index.html index.htm; #------------------------------------------------------------------------ # SSL # ssl_certificate /etc/letsencrypt/live/votre-domaine.tld/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/votre-domaine.tld/privkey.pem; }
Block serveur HTTPS – NGINX ≥ 1.9.5
Si NGinx est au moins installé dans sa version 1.9.5, vous pouvez activer le protocole HTTP2, celui-ci remplace SPDY. Voici donc comment je vous conseille de configurer HTTPS.
#------------------------------------------------------------------------ # BLOCK SERVEUR HTTPS # server { listen 443 ssl http2; server_name www.votre-domaine.tld; root /var/www/www.votre-domaine.tld; index index.php index.html index.htm; #------------------------------------------------------------------------ # SSL # ssl_certificate /etc/letsencrypt/live/votre-domaine.tld/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/votre-domaine.tld/privkey.pem; }
Sécuriser HTTPS en limitant les protocoles
Maintenant que vous avez compris comment déployer HTTPS sur votre site nous allons nous attaquer à sa sécurisation.
Commençons par limiter les protocoles acceptés par notre serveur. Si vous souhaitez ABSOLUMENT obtenir la meilleure note possible sur certains outils de benchmarking comme SSLABS et celui d’Aeris, il vous faudra vous limiter au protocole TLSv1.2
ssl_protocols TLSv1.2;
Chez moi je désactive totalement le protocole SSL et j’accepte tous les protocoles TLS afin de garantir un accès au plus grand nombre.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
Sécuriser HTTPS : Configurer l’échange de clé Diffie-Hellman (DH)
Par défaut la clé est une clé de 2048 bits. Générez donc une de 4096 (attention cela peut prendre du temps, en particulier sur un VPS peu puissant)
mkdir /etc/nginx/ssl cd /etc/nginx/ssl openssl dhparam 4096 -out dh4096.pem
Configurez cette nouvelle clé dans votre virtualhost en ajoutant la ligne suivante
ssl_dhparam /etc/nginx/ssl/dh4096.pem;
Je vous conseille de baser l’échange sur des courbes elliptiques en ajoutant
ssl_ecdh_curve secp384r1;
Sécuriser HTTPS : Configurer les ciphers
J’indique à NGinx d’utiliser mes ciphers en utilisant le code suivant
ssl_prefer_server_ciphers on;
Puis je configure les ciphers :
Si vous utilisez seulement le protocole TLSv1.2
ssl_ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256";
Si vous utilisez les protocoles TLSv1 TLSv1.1 et TLSv1.2
ssl_ciphers "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:";
Sécuriser HTTPS : OSCP Stapling
L’OSCP est un protocole permettant de vérifier si un certificat est valide ou révoqué. Voici comment on le configure :
On ajoute une variable ssl_trusted_certificate
que l’on fait pointer vers les certificats de l’autorité de certification
ssl_trusted_certificate /etc/letsencrypt/live/votre-domaine.net/chain.pem;
Ensuite il faut configurer les serveurs DNS que l’on utilise pour résoudre le nom de domaine du serveur OSCP (Ici ceux de la FDN, mais vous pouvez en utiliser d’autres)
resolver 80.67.169.12 80.67.169.40 valid=300s; resolver_timeout 5s;
Sécuriser HTTPS : HSTS
HSTS ou HTTP Strict Transport Security est un mécanisme de politique de sécurité proposé pour HTTP, permettant à un serveur web de déclarer à un agent utilisateur (comme un navigateur web), compatible, qu’il doit interagir avec lui en utilisant une connexion sécurisée. (Wikipédia).
En clair après une seule visite il n’y aura plus de redirection http vers https, le navigateur web s’en souviendra. Lorsque HSTS est activé tous les liens vers des ressources non-sécurisés de votre domaine seront donc remplacés par des liens sécurisés.
Pour activer HSTS pour un an ajoutez
add_header Strict-Transport-Security "max-age=31536000";
Pour l’activer aussi sur vos sous domaines
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains"
On peut encore faire mieux : utiliser HSTS Preload.
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
Si vous choisissez d’utiliser HSTS Preload, vous pouvez ajouter votre domaine à cette liste. Dès lors, même sans avoir visité votre site le navigateur d’un utilisateur saura qu’il doit se connecter en HTTPS.
Si vous ajoutez votre site à cette liste votre domaine et ses sous-domaines devront TOUJOURS utiliser HTTPS
Sécuriser HTTPS : Session ID et Session Tickets
Pour configurer la mise en cache des paramètres HTTPS ajoutez une variable ssl_session_cache
.
ssl_session_timeout 5m; ssl_session_cache shared:SSL:10m;
Pour plus de sécurité vous pouvez choisir de désactiver les Session Tickets (tickets de session) . Les tickets session permettent de reprendre une connexion TLS. Ainsi même si le clé privée de chiffrement est compromise vous garantissez la confidentialité des communications passées.
ssl_session_tickets off;
Sécuriser HTTPS : Content Security Policy
Content Security Policy (CSP) permet de restreindre l’origine du contenu affiché sur votre site (javascript, css, polices) à seulement certains sites autorisés. Cela vous permet donc de mieux vous protéger contre des failles XSS.
Par exemple la configuration ci dessous vous permettra d’autoriser le chargement de contenu depuis :
- votre site
- gravatar – avatar wordpress
- gtstatic : nécessaire pour l’administration de wordpress
- google api : google fonts
- google analytics
add_header Content-Security-Policy "default-src 'self' https://*.gravatar.com https://*.gstatic.com https://*.googleapis.com https://ssl.google-analytics.com https://s-static.ak.facebook.com https://www.google-analytics.com data: 'unsafe-inline' 'unsafe-eval'";
Vous pouvez aussi être averti en cas de violation de vos Content Security Policy
Pour cela il vous suffit de créer un compte sur Report-uri.io puis d’accèder aux options
Créez vous un URL personnalisé en indiquant le nom que vous souhaitez lui donner, ici monurl
Voici vos urls personnalisées
Modifiez maintenant votre en-tête Content Security Policy et ajoutez la première url de report comme ci-dessous
add_header Content-Security-Policy "default-src 'self' https://*.gravatar.com https://*.gstatic.com https://*.googleapis.com https://ssl.google-analytics.com https://s-static.ak.facebook.com https://www.google-analytics.com data: 'unsafe-inline' 'unsafe-eval'; report-uri https://monurl.report-uri.io/r/default/csp/enforce";
Puis ajoutez une en-tete Content Security Policy Report Only et ajoutez la seconde url (reportOnly)
add_header Content-Security-Policy-Report-Only "default-src 'self' https://*.gravatar.com https://*.gstatic.com https://*.googleapis.com https://ssl.google-analytics.com https://s-static.ak.facebook.com https://www.google-analytics.com data: 'unsafe-inline' 'unsafe-eval'; report-uri https://monurl.report-uri.io/r/default/csp/reportOnly";
Sécuriser HTTPS : X-Frame-Options
L’en-tête X-Frame-Options permet de vous assurer que le contenu de vos pages ne soit pas afficher sur un autre site.
Il existe plusieurs options de configuration pour cet entête :
- DENY : Les pages ne pourront pas etre affichée dans une frame ou iframe
- SAMEORIGIN : Les pages pourront etre autorisée mais seulement sur le même site
- ALLOW-FROM uri : La page ne pourra être affichée dans une balise que si elle provient de l’origine spécifié
De mon coté je vous conseille d’ajouter la configuration suivante
add_header X-Frame-Options SAMEORIGIN;
Sécuriser HTTPS : X-Content-Type-Options
L’en-tête X-Content-Type-Options vous permet de rejeter des éléments avec des types MIME incorrects et donc de vous prémunir d’une attaque drive-by-download.
La seule configuration possible pour cette en-tête est la suivane
add_header X-Content-Type-Options nosniff;
Sécuriser HTTPS : X-XSS-Protection
Cet en-tête permet d’activer les filtres anti-xss présent sur certains navigateurs.
Voici la configuration à utiliser
add_header X-XSS-Protection "1; mode=block";
Sécuriser HTTPS : En-tête Server
Cet en-tête est ajouté automatiquement par NGINX. Il est cependant possible de la modifier si et seulement si le mod ngx_headers_more
est installé sur votre serveur NGINX.
Pour savoir si ce mod est disponible sur votre serveur utilisez la commande suivante
nginx -V
Ici le mod ngx_headers_more est bien installé
root@noobunbox:~$ nginx -V nginx version: nginx/1.9.12 built by gcc 4.9.2 (Debian 4.9.2-10) built with OpenSSL 1.0.2g 1 Mar 2016 TLS SNI support enabled configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --user=www-data --group=www-data --with-threads --with-http_ssl_module --with-http_v2_module --with-ipv6 --with-http_mp4_module --with-http_auth_request_module --with-http_slice_module --with-file-aio --with-cc-opt='-g -O2 -fstack-protector-strong -Wformat -Werror=format-security' --add-module=/opt/ngx_brotli --add-module=/opt/headers-more-nginx-module-0.29rc1
Nous pouvons donc ajouter la directive suivante à notre configuration
more_set_headers "Server: Mon server";
Sécuriser HTTPS : X-Powered-By
Cet en-tête indique la version de PHP utilisée sur votre serveur si vous n’avez pas configuré la directive expose_php
sur off
dans votre fichier php.ini.
Vous pouvez configurer modifier cet en-tête en ajoutant la configuration suivante
add_header X-Powered-By "Ce que vous voulez";
Sécuriser HTTPS : Résumé de la configuration
Résumé de ma configuration :
Attention si vous souhaitez seulement autoriser les connexions via le protocole TLSv1.2 n’oubliez pas de modifier les variables ssl_protocols
et ssl_ciphers
#------------------------------------------------------------------------ # REDIRECTION HTTPS # server { listen 80 default_server; listen [::]:80 default_server; server_name www.votre-domaine.tld; #Dé-commentez la ligne suivante si et seulement si le mod ngx_headers_more est installé #more_set_headers "Server: Mon server"; return 301 https://www.votre-domaine.tld$request_uri; } #------------------------------------------------------------------------ # BLOCK SERVEUR HTTPS # server { listen 443 ssl http2; server_name www.votre-domaine.tld; root /var/www/www.votre-domaine.tld; index index.php index.html index.htm; #------------------------------------------------------------------------ # SSL # ssl_certificate /etc/letsencrypt/live/votre-domaine.tld/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/votre-domaine.tld/privkey.pem; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_dhparam /etc/nginx/ssl/dh4096.pem; ssl_ecdh_curve secp384r1; ssl_prefer_server_ciphers on; ssl_ciphers "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA"; # ssl optimizations ssl_session_timeout 5m; ssl_session_cache shared:SSL:10m; ssl_session_tickets off; add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload"; ssl_stapling on; ssl_stapling_verify on; ssl_trusted_certificate /etc/letsencrypt/live/votre-domaine.tld/chain.pem; resolver 80.67.169.12 80.67.169.40 valid=300s; resolver_timeout 15s; #------------------------------------------------------------------------ # SECURITY add_header Content-Security-Policy "default-src 'self' https://*.gstatic.com https://*.googleapis.com data: 'unsafe-inline' 'unsafe-eval'; report-uri https://report-uri.io/report/monurl"; add_header Content-Security-Policy-Report-Only "default-src 'self' https://*.gravatar.com https://*.gstatic.com https://*.googleapis.com https://ssl.google-analytics.com https://s-static.ak.facebook.com https://www.google-analytics.com data: 'unsafe-inline' 'unsafe-eval'; report-uri https://report-uri.io/report/monurl/reportOnly"; #Dé-commentez la ligne suivante si et seulement si le mod ngx_headers_more est installé #more_set_headers "Server: Mon server"; add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header X-Powered-By "Ce que vous voulez ?"; }
Sécuriser HTTPS : Tester votre configuration
Pour vérifier si vous avez bien sécuriser HTTPS vous pouvez tester votre configuration sur plusieurs sites.
SSL Lab
Surement le plus connu. Son plus grand avantage réside dans le fait qu’il vous indique quels sites et navigateurs pourront consulter votre site au vu de votre configuration.
Voici la note que vous devriez obtenir si vous avez choisi de n’accepter que les connexions via le protocole TLSv1.2 :
Voici la note que vous devriez obtenir si vous avez choisi d’accepter les connexions via les protocoles TLSv1 TLSv1.1 TLSv1.2 :
CryptCheck
Un outil mis à votre disposition par Aeris.
Voici la note que vous devriez obtenir si vous avez choisi de n’accepter que les connexions via le protocole TLSv1.2
Voici la note que vous devriez obtenir si vous avez choisi d’accepter les connexions via les protocoles TLSv1 TLSv1.1 TLSv1.2
SecurityHeaders
Avec SecurityHeaders votre note n’est déterminée que par les en-tètes transmissent par votre serveur. Ici rien concernant les ciphers, l’oscp ou autre.
Si vous avez bien suivi ce guide voici la note que vous devriez obtenir :
Sources
- Angristan, Configurer HTTPS sur NGinx
- Mozilla, Mozilla TLS Generator
- Lexsi Security Hub, En-têtes et vous ?
Merci pour ce mémo ! Il m’a réellement servit de base au niveau de la sécurisation d’HTTPS sur mon serveur. Je suis passé de plus aux certificats ECDSA, adieu totalement le RSA 🙂 . De plus j’utilisais comme ciphers ceci : EECDH+AESGCM:EECDH+AES mais depuis peu je suis passé à ceci : EECDH+AESGCM car j’ai vu sur l’outils d’Aeris que l’AES avec mode CBC était de couleur rouge maintenant 😉 . Pour voir la différence, voici l’analyse de deux de mes sites : – Sans AES mode CBC -> https://tls.imirhil.fr/https/www.qwaser.fr – Avec AES mode CBC -> https://tls.imirhil.fr/https/cloud.qwaser.fr De plus au niveau… Lire la suite »
Re 🙂
Oui Aeris a modifié cryptcheck, et ce n’est pas totalement fini. Les notations vont encore évoluées.
Tu peux avoir plusieurs ssl_ecdh-curve seulement si tu as compilé NGinx avec OpenSSL 1.0.2. voir ici
Ah en plus ce n’est pas finit ? Alors je vais rester attentif aux futurs évolutions 😀 Je souhaite rester le plus sécurisé possible pour mes sites, quitte à restreindre son accès aux anciennes technologies 😉 Il faut qu’Nginx soit compiler obligatoirement ? Car avec les backports j’ai installer OpenSSL 1.1 et Nginx je l’ai passé en mainline, mais ce qui est écrit dans la documentation officielle ne fonctionne pas apparemment donc c’est pour ça que je ne comprends pas… D’autant que si je dois commencer à compiler Nginx, à la limite je préfère le faire avec LibreSSL, de ce… Lire la suite »
Par exemple pour l’instant cryptcheck ne sanctionne pas l’utilisation de CBC mais ce sera bientôt le cas. Si tu as déjà supprimé les ciphers utilisant CBC ta note ne baissera pas.
Oui pour pouvoir utiliser plusieurs courbes ECDH il faut compiler sa propre version d’NGinx avec OpenSSL 1.1.
Les versions des dépôts backports ou officiels ont été compilées avec OpenSSL 1.0. C’est pourquoi meme en installant OpenSSL 1.1 cela ne fonctionnera pas.
LibreSSL est un fork d’OpenSSL sensé corrigé certains bugs de ce dernier. Jusqu’à ce qu’OpenSSL passe en version 1.1 c’est avec lui que je compilais NGinx.
Ah je comprends mieux alors, merci pour ces indications :). Donc depuis OpenSSL 1.1.0 tu as arrêté de compiler Nginx avec LibreSSL si j’ai bien compris. Pour quel motif es-tu revenu à OpenSSL du coup ? Sinon au niveau de la compilation d’Nginx, si je le compile avec OpenSSL 1.1.0 mais que j’ai OpenSSL 1.0 d’installé cela créera-t-il des problèmes ? Et comment se passe les mises à jours du coup ? Car j’aimerai vraiment pouvoir mettre plusieurs courbes ECDH car j’ai pu constater que ceux chez Free et Bouygues ne pouvais pas accéder à mes sites si je mettais… Lire la suite »
Je suis repassé à OpenSSL pour pouvoir profiter de la possibilité d’utiliser différentes courbes.
Non ça ne devrait pas poser de problème.
Pour les mises à jours il faut recompiler. De mon côté j’utilise un script. https://github.com/stylersnico/nginx-openssl-chacha-naxsi
Ce n’est pas une histoire de FAI mais de navigateur / OS.
Ah oui, une possibilité non négligeable en effet :).
D’accord, dans ce cas je vais voir pour y passer alors ;). Et donc à chaque mise à jours d’Nginx et/ou OpenSSL il faut relancer le script ? Ou le retélécharger et ensuite le relancer ? Car je vois qu’il va chercher en dur des versions précises d’Nginx et OpenSSL :).
Bah je pensais aussi que ça venait de leur navigateur, pourtant même sur un Chrome à jour ça ne passait pas chez eux donc j’ai pas compris Oo.
L’auteur du script est très réactif pour les mises à jour. Il suffit ensuite de le re-télécharger.
D’accord, vraiment merci pour tes réponses !
J’ai donc exécuter le script et j’en ai profiter pour retravailler un peu mes fichiers de configurations. Sachant que tous mes sites utilisent les mêmes paramètre TLS par exemple, j’ai commencé à mettre tous les paramètre communs dans nginx.conf et j’ai vu que le script avait déjà bien mâché le travail.
Je me suis juste permit d’essayer de commenter chaque ligne pour comprendre leur rôle, car j’estime que c’est important de le connaitre. Voici le résultat si ça t’intéresse : http://hastebin.com/ehuderogic.coffee
Pas de problème 🙂
J’ai fait fait exactement la même chose sur mes serveurs. Ça ne sert pas à grand chose de multiplier les configurations identiques.
Me fait penser qu’il faudrait que je remette ce post à jour. Peut être la semaine prochaine. 🙂
Hâte de voir la mise à jour de ce post alors ! En tout cas je suis content maintenant sur mon site j’utilise su CHACHA20 + POLY1305 ce que je pensais pas encore possible 😮 Et l’échange de clé commence avec une courbe de 384 bits pour aller jusqu’à 571 bits et prends en charge la curve 25519 qui plus est ! Celle qu’Aeris recommande même avant le ECDSA 😉
https://tls.imirhil.fr/https/www.qwaser.fr
Petite erreur dans mes précédents commentaires, la nouvelle version d’OpenSSL est la 1.1.0 et non pas la 1.0.2
EDIT : Modification des commentaires effectuée