Comment sécuriser votre appli Shiny avec le HTTPS

Cet article fait partie de la série Comment déployer une application Shiny sur AWS, divisée en 7 parties. Pour accéder aux autres parties, utilisez le sommaire ci-dessous:


Dans le dernier article, on a changé l’adresse IP un peu moche de notre appli Shiny en bon nom de domaine et on était capable d’accéder à l’application en tapant une URL du type http://shiny.charlesbordet.com/movie-explorer.

Très cool.

Sauf qu’il reste un vrai problème si vous voulez mettre votre application en production pour un client.

L’appli n’est pas sécurisée.

Vous avez peut-être remarqué le message sympa que vous laisse votre navigateur :

Shiny AWS HTTP non sécurisé

Votre application utilise en effet le HTTP, un protocole de communication non chiffré, alors qu’aujourd’hui, tout le monde utilise HTTPS, l’équivalent chiffré et sécurisé.

Dans cet article, on va voir :

  • Qu’est-ce que le HTTPS et pourquoi on a besoin d’y passer
  • Comment créer des certificats SSL/TLS automatiquement et gratuitement avec Let’s Encrypt
  • Comment installer ces certificats sur le serveur
  • Comment forcer l’utilisation de HTTPS

Le HTTPS, c’est quoi ?

Vous en avez sans doute entendu parler sans savoir exactement comment le https fonctionnait ni pourquoi il était autant nécessaire.

Imaginez qu’à chaque fois que vous vous connectez à un site web, vous envoyez une requête de chez vous jusqu’au data center où le serveur est hébergé, puis une réponse revient chez vous.

Ces messages font parfois le tour du monde et passent de main en main, de routeur en routeur, avant d’arriver à destination.

Quand on utilise le HTTP, ces messages ne sont pas chiffrés.

Donc n’importe qui peut à tout moment intercepter le message, l’ouvrir, le lire, et même le modifier avant de le renvoyer sans que personne ne le sache.

Shiny AWS Man in the Middle Attack

On appelle ça une Man-in-the-Middle attack .

Pas très cool, d’autant plus quand le message contient des mots de passe, des data confidentielles ou votre code de carte bleue.

C’est pour éviter ça que le HTTPS est apparu.

Le HTTPS garantit que les messages seront chiffrés. Si une personne intercepte le message, elle ne pourra pas le lire.

Au départ, le HTTPS était surtout utilisé par les commerces en ligne pour sécuriser vos informations bancaires lors de transactions, puis il a été généralisé à l’Internet entier.

C’est pour ça que votre navigateur vous met un gros panneau “ATTENTION DANGER !!!”.

Bref, si vous voulez :

  • que vos données soient sécurisées et chiffrées,
  • que votre client ou chef ne flippe pas en voyant un avertissement de la part du navigateur,

On va devoir utiliser le HTTPS.

Comment ça se passe ?

Il faut d’abord obtenir des certificats (SSL/TLS) pour le serveur et des clés.

Je vous passe les détails techniques de cryptographie, mais en gros le certificat permet de dire « Oui c’est bien moi le vrai serveur ! » et il contient une clé de chiffrement.

Créer un certificat SSL/TLS avec Let’s Encrypt

Les certificats s’obtiennent auprès d’institutions de confiance.

La plupart du temps, c’est payant.

Si on utilise Let’s Encrypt, c’est gratuit et automatique.

Donc je vous propose d’utiliser Let’s Encrypt.

Il existe même un programme qui permet de renouveler les certificats automatiquement (parce qu’ils ont une date de péremption, oui).

Donc on le fait une fois, et après on est tranquille !

Pour commencer, allons sur le site de Cerbot:.

Dans les cases en haut, on choisit :

  1. Software = Nginx
  2. System = Ubuntu 18.04 (prenez celui que vous utilisez évidemment)

Shiny AWS Certbot

Ensuite descendez dans la page et copiez/collez les instructions dans la console de votre serveur. Vous pouvez jeter un œil sur l’article Installer R et R Shiny sur le serveur si vous avez déjà oublié comment vous connecter à votre serveur.

Donc :

$ sudo apt-get update
$ sudo apt-get install software-properties-common
$ sudo add-apt-repository universe
$ sudo add-apt-repository ppa:certbot/certbot
$ sudo apt-get update

$ sudo apt-get install certbot python-certbot-nginx 

L’obtention du certificat est ensuite automatique en tapant la commande suivante :

$ sudo certbot certonly --nginx -d shiny.charlesbordet.com

Évidemment, modifiez le nom de domaine pour renseigner le vôtre.

Quelques lignes vont s’afficher en parlant de challenges :

Renewing an existing certificate
Performing the following challenges:
http-01 challenge for shiny.charlesbordet.com
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/shiny.charlesbordet.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/shiny.charlesbordet.com/privkey.pem
   Your cert will expire on 2020-02-04. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"

Les lignes les plus importantes sont celles qui vous informent que les certificats ont été enregistrés dans le dossier /etc/letsencrypt/live/shiny.charlesbordet.com.

On va pouvoir s’en servir pour passer notre appli en HTTPS !

Comment installer un certificat SSL/TLS avec nginx

Pour mettre en place ce qui suit, il faut que vous ayez suivi les recommandations du précédent article précédent de cette série sur la création d’un nom de domaine, notamment la dernière section : Mettre en place un reverse proxy.

Si vous ne l’avez pas encore mis en place, je vous invite à le faire et revenir ensuite.

Dans cette section, on avait installé le logiciel nginx.

Ce logiciel, c’est tout simplement ce qu’on appelle un serveur web. L’une de ses fonctionnalités, c’est le reverse proxy (ce qu’on avait donc vu la dernière fois).

Rappelez-vous, on avait dit que nginx agissait comme le gardien du serveur : il écoute sur le port 80, et il redirige les visiteurs vers le bon port (3838 pour Shiny par exemple).

Le port 80, c’était pour le protocole HTTP.

Comme on va passer en HTTPS, ce port va changer : à la place, on va utiliser le port 443.

Chaque protocole a son port dédié, ce n’est pas nous qui pouvons décider. Par exemple, SSH est sur le port 22, FTP sur le port 21, etc.

Vous pouvez trouver une liste de ces ports logiciels sur Wikipédia.

Ils sont pris jusqu’au port 1024, et tout ceux qui sont au-delà sont libres. C’est pour cette raison que Shiny s’octroie le port 3838 par défaut (mais on pourrait le changer).

Bref.

Reprenons la configuration de nginx. On va changer le port 80 en 443.

Pour rappel, le fichier de configuration se trouve au chemin suivant :

sudo nano /etc/nginx/sites-available/shiny.conf

Vous devriez retrouver le fichier qu’on avait écrit la dernière fois.

On va rajouter et modifier quelques points.

1. Changement de port

D’abord, on change le port.

Les deux lignes qui commencent par listen vont être remplacées par celles-ci :

# listen 443 signifie que le serveur Nginx écoute sur le port 443 (HTTPS).
listen 443 ssl http2;
listen [::]:443 ssl http2;

2. Configuration du SSL

On va ensuite ajouter les paramètres de SSL.

SSL, c’est le protocole qui permet de chiffrer vos données. Dites-vous que HTTP + SSL = HTTPS.

Normalement, depuis 20 ans, on dit TLS, mais tout le monde continue de dire SSL.

Je vous le dis tout de suite : je ne suis pas un expert de cryptographie ni de SSL.

Donc quand il s’agit de configurer de manière optimale, je vais chercher l’information d’une source sûre.

Ici, j’utilise les recommandations de Mozilla. Ils ont créé un SSL Configuration Generator.

On va donc rajouter les lignes suivantes dans notre fichier de config :

# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_certificate /path/to/signed_cert_plus_intermediates;
ssl_certificate_key /path/to/private_key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;  # about 40000 sessions
ssl_session_tickets off;

# curl https://ssl-config.mozilla.org/ffdhe2048.txt > /path/to/dhparam.pem
ssl_dhparam /path/to/dhparam.pem;

# intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

# HSTS (ngx_http_headers_module is required) (63072000 seconds)
add_header Strict-Transport-Security "max-age=63072000" always;

# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;

# verify chain of trust of OCSP response using Root CA and Intermediate certs
ssl_trusted_certificate /path/to/root_CA_cert_plus_intermediates;

Dans cette config, il va falloir modifier certaines choses.

On commence avec les deux premières lignes ssl_certificate et ssl_certificate_key. Il faut leur donner les chemins des certificats générés plus tôt.

Je les modifie ainsi :

ssl_certificate /etc/letsencrypt/live/shiny.charlesbordet.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/shiny.charlesbordet.com/privkey.pem;

(Bien entendu, remplacez avec votre nom de domaine)

3. Télécharger les paramètres DH

On va ensuite s’attaquer aux paramètres DH. Le commentaire nous dit de les télécharger à partir d’une URL. C’est donc ce qu’on va faire avec l’instruction suivante dans la console :

$ curl https://ssl-config.mozilla.org/ffdhe2048.txt > /etc/nginx/snippets/dhparam.pem;

Et il faut ensuite modifier le chemin spécifié dans ssl_dhparam :

ssl_dhparam /etc/nginx/snippets/dhparam.pem;

Je vous conseille de désactiver la partie HSTS pour l’instant.

Pourquoi ? Car le HSTS va permettre de dire au navigateur : « À partir d’aujourd’hui, et pendant deux ans, tu utilises toujours le HTTPS et jamais le HTTP. ».

C’est une bonne pratique de sécurité.

Sauf que si vous avez des problèmes pour mettre en place le HTTPS et que vous souhaitez revenir au HTTP, vous allez être coincé.

Donc désactivons-le pour le moment, et lorsqu’on sera sûr que la config fonctionne bien, on le remettra. Je rajoute simplement un # devant :

# add_header Strict-Transport-Security "max-age=63072000" always;

4. Utiliser le certificat Let’s Encrypt

Il ne nous reste plus qu’à modifier la dernière ligne avec un certificat de Let’s Encrypt :

ssl_trusted_certificate /etc/letsencrypt/live/shiny.charlesbordet.com/chain.pem;

Et voili voilou !

À présent, enregistrez la configuration, on va tester si tout fonctionne.

Pour vérifier que vous n’avez pas fait d’erreur, tapez dans la console :

sudo nginx -t

Vous devriez recevoir le message suivant :

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Si ce n’est pas le cas, c’est que vous avez fait une erreur quelque part.

Une fois la syntaxe validée, on redémarre le serveur nginx :

sudo systemctl restart nginx

À présent, il ne reste plus qu’à tester.

Rendez-vous sur votre nom de domaine en tapant bien https au début, par exemple : https://shiny.charlesbordet.com/movie-explorer.

Votre navigateur va vous afficher un beau cadenas avec un message rassurant :

Shiny AWS HTTPS sécurisé

Bravo !

Mais ce n’est pas terminé. Il nous reste encore une dernière étape : interdire le HTTP et forcer l’utilisation du HTTPS.

Comment forcer l’utilisation de HTTPS

Un peu plus haut, je vous ai recommandé de désactiver le HSTS (l’utilisation automatique du HTTPS).

On va à présent réactiver cette option.

Dans la config nginx, on retire le # ajouté plus tôt :

add_header Strict-Transport-Security "max-age=63072000" always;

Et on va faire une deuxième modification.

Au niveau du générateur de configuration SSL de Mozilla, vous avez peut-être remarqué un autre bloc server ajouté en haut de leur fichier.

Ce bloc serveur va écouter sur le port 80, c’est-à-dire HTTP, et automatiquement rediriger vers le port 443.

Il permet aux utilisateurs qui viennent sur votre appli en HTTP de ne pas tomber sur une page 404 (ben oui, parce que plus personne n’écoute sur le port HTTP à présent).

À la place, on leur dit gentiment d’aller voir en 443 si on y est.

Donc tout en haut du fichier de config nginx, on va rajouter les lignes suivantes :

server {
    listen 80;
    listen [::]:80;

    # redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
    return 301 https://$host$request_uri;
}

Et là, c’est bon. On a activé le HTTPS, notre appli est sécurisée, et les données qu’on envoie sont chiffrées.

N’oubliez pas de redémarrer nginx à chaque fois que vous faites un changement :

sudo systemctl restart nginx

Vous aurez remarqué que plus on avance dans cette série d’articles, plus on s’éloigne de R et plus on rentre dans le domaine de l’administration système.

Perso, je trouve que c’est un domaine super intéressant.

Grâce à Shiny, j’ai appris énormément sur les serveurs et même sur le fonctionnement des internets en général.

N’hésitez pas à me dire en commentaire si vous avez réussi à installer le HTTPS, ou bien si vous rencontré un souci !

Il ne nous reste plus qu’un sujet à aborder dans ce dossier dédié au déploiement d’une appli Shiny sur AWS : Comment protéger l’accès à l’application avec un mot de passe.

Mis à jour :

Commentaires

Emmanuel Bonnet

Super tuto très clair même pour un néophyte! Où l’on apprend comment déployer gratuitement (à un nom de domaine près si on veut aller jusqu’au bout) une appli shiny sur le web et en https! BRAVO Charles

Laisser un commentaire

Les champs obligatoires sont marqués *

Chargement...

Les commentaires sont validés manuellement. La page va se rafraîchir après envoi.