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 ?