Sécuriser HTTPS – NGinx

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
  • facebook
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

Sécuriser HTTPS

 

Voici vos urls personnalisées

Sécuriser HTTPS

 

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 :

Sécuriser HTTPS

 

Voici la note que vous devriez obtenir si vous avez choisi d’accepter les connexions via les protocoles TLSv1 TLSv1.1 TLSv1.2 :

Sécuriser HTTPS

 

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

Sécuriser HTTPS

 

Voici la note que vous devriez obtenir si vous avez choisi d’accepter les connexions via les protocoles TLSv1 TLSv1.1 TLSv1.2

Sécuriser HTTPS

 

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 :

Sécuriser HTTPS

 

Sources