SwordArMor

Comprendre les grands principes du SSL/TLS

Histoire et terminologie

SSL est un acronyme anglais voulant dire Secure Sockets Layer. Il a été initialement développé par Netscape Communications (qui a développé Netscape Navigator qui a servi de base à Mozilla pour firefox). Il existe en trois versions : SSLv1, SSLv2 et SSLv3. Aucune de ces versions n’est plus utilisée de nos jours (ou alors par des gens inconscients) car il est possible d’utiliser des failles inhérentes au protocole afin de déchiffrer le trafic.
Il a ensuite été remplacé par TLS (pour Transport Layer Security). Ce dernier existe également en trois versions : TLSv1.0, TLSv1.1 et TLSv1.2. Une version 1.3 est actuellement en cours de développement mais encore au stade de brouillon à l’IETF.
Par abus de langage, et comme je vais le faire tout au long de cet article, on utiliser souvent le terme SSL pour parler de TLS.

Cas d’usage

Le SSL est un standard de chiffrement très utilisé sur Internet. Par exemple, vous l’utilisez en visitant ce blog ainsi que tous les sites dont l’URL commence par https://.
Il est également utilisé dans le mail, avec jabber, sur IRC, etc.

Grands principes

Chiffrement

Comme beaucoup de protocoles de chiffrement de nos jours, SSL se base sur de la cryptographie asymétrique, c’est à dire avec une clé publique et une clé privée. La clé publique est le certificat, généré par une autorité de certification comme nous le verrons plus tard et la clé privée est générée (ainsi que le CSR) par l’administrateur du serveur et y reste ; c’est le propre d’une clé privée, sinon elle n’est plus tellement privée et il y a un souci. Comme toujours, la clé publique est dérivée de la clé privée et l’une ne peut pas fonctionner sans l’autre.
Cependant, comme le chiffrement asymétrique coûte cher en ressources, une fois la session établie entre le client et le serveur, une clé symétrique est négociée pour une durée limitée et c’est cette clé qui servira à chiffrer les données qui transitent.
Nous n’utilisons pas une clé symétrique dès le début car dans ce cas, tout le monde devrait connaître cette clé pour déchiffrer du contenu, et donc tout le monde pourrait ré-utiliser cette clé pour se faire passer pour le serveur (et donc intercepter le trafic chiffré et en connaître le contenu).

Authentification

Dans un contexte de chiffrement se pose toujours la question de l’authenticité du pair avec lequel on discute. En effet, ce n’est pas le tout de chiffrer pour empêcher les grandes oreilles de mettre leur nez où elle ne devrait pas, mais encore faut il le faire avec la bonne personne.
Admettons que vous chiffriez une conversation mais en se connectant au mauvais serveur. Ce serveur peut très bien déchiffrer ce qui passe, le lire et le re-chiffrer pour l’envoyer à qui de droit. Tout l’intérêt du chiffrement est donc perdu et personne ne se rendrait compte de la supercherie.
Généralement, on se contente de vérifier l’identité du serveur, mais on peut aussi vérifier celle du client. Dans le cas du serveur, on se base généralement sur le nom de domaine (www.swordarmor.fr pour ce qui nous concerne) et dans le cas du client on se base généralement sur l’adresse mail (alarig@swordarmor.fr en ce qui me concerne).

Afin de répondre à ce problème, nous faisons intervenir un tiers de confiance (sic), ce sont les fameuses CA – pour Certification Autorities en anglais, soit autorités de certification en français. Il s’agit de grands organismes en charge de distribuer – souvent contre finances – des certificats x509 (du nom de la norme qui les défini) aux personnes physiques ou morales qui en font la demande. On dit aussi « signer un certificat ».
Ces organismes ont leur propre certificat déjà enregistré dans les navigateurs (dans le cas du web, mais c’est le cas pour tout client supportant le SSL). Votre certificat ayant été signé par une de ces entités, le navigateur peut vérifier sa validité en établissant une chaîne de confiance. Théoriquement, ça semble sympathique, sauf qu’en fait non. Toute CA enregistrée dans le navigateur peut émettre n’importe quel certificat, y compris des certificats pour des noms de domaines qui ont déjà été singés. Ainsi, une autorité peu scrupuleuse peut parfaitement fournir des certificats valides sans se préoccuper de savoir si la personne qui en fait la demande le fait légitimement ou non. On peut par exemple citer Symantec qui signe des certificats pour les proxy de Blue Coat Systems, ces derniers pouvant donc intercepter le trafic SSL qui y passe ; et on sait que cette entreprise est pour le moins controversée.

Intégrité

SSL est aussi en charge de s’assurer que les données n’ont pas été altérées entre ce que le serveur a envoyé et ce que le client a reçu. En effet, il est nécessaire de s’assurer que les données reçues par le client sont bien celles envoyées par le serveur et qu’elle n’ont pas été modifiées sur le chemin.
C’est là qu’intervient la HMAC. Il s’agît d’un calcul mathématique relativement compliqué se basant sur une fonction de hachage cryptographique combiné avec une clé secrète.

En pratique

Afin de proposer votre site en HTTPS, vous devez disposer d’un HTTPd compatible avec le SSL. C’est le cas de la plupart d’entre eux. Pour ne pas m’éterniser sur des exemples de configuration, je me contenterai de nginx et apache2.

Génération d’un CSR et d’une clé privée

Le CSR est un fichier que devrez envoyer à l’autorité de certification afin qu’elle vous signe un certificat. L’acronyme CSR a pour signification Certificate Signing Request, soit requête de signature de certificat. Ce CSR doit à minima contenir le nom qui doit être certifié (soit le nom DNS du site dans notre cas) mais il est possible d’y ajouter d’autres champs comme la localisation ou l’identité de l’organisation qui demande le certificat. Le FQDN doit être donné à la question « Common Name (e.g. server FQDN or YOUR name) ». Nous devons aussi générer la clé privée associée au futur certificat.
La génération se fait avec openssl (ou libressl ;).

openssl req -nodes -newkey RSA:4096 -keyout
      exemple.org.key -out exemple.org.csr
Une fois que le CSR est généré, transmettez le à l’autorité de certification et elle vous donnera un certificat. Placez le tout dans /etc/ssl/httpd/.
Ensuite, assurez vous que la clé ne soit pas ouverte à tous vents. Un petit chmod 600 /etc/ssl/httpd/exemple.org.key ne fera pas de mal.

Je ne copie ici que la partie spécifique au SSL, le reste du fichier de configuration est le même.

nginx

server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;

	ssl_certificate		/etc/ssl/httpd/exemple.org-chained.crt;
	ssl_certificate_key	/etc/ssl/nginx/exemple.org.key;
Nous remarquons que j’ai donné le fichier /etc/ssl/httpd/exemple.org-chained.crt et non /etc/ssl/httpd/exemple.org.crt comme certificat. En effet, afin de reconstituer la chaîne de certificats, le client HTTP a besoin que l’on lui envoie le certificat racine de la CA qui a signé le certificat. On chaîne donc les deux certificats dans un seul. Pour cela il faut télécharger le certificat racine depuis le site de la CA puis concaténer les deux :
cat /etc/ssl/httpd/exemple.org.crt $root_ca_cert.crt > /etc/ssl/httpd/exemple.org-chained.crt
Les plus avisés d’entre vous auront peut-être remarqué le http2 sur les lignes listen. Ce mot clé indique à nginx de supporter le protocole HTTP/2 en plus de HTTP/1.0 et HTTP/1.1 ; il est optionnel.

apache2

<VirtualHost *:443>
	SSLEngine		on
	SSLCertificateFile	/etc/ssl/httpd/exemple.org.crt
	SSLCertificateKeyFile	/etc/ssl/nginx/exemple.org.key
	SSLCACertificateFile	/etc/ssl/certs/$root_ca_cert.crt
	SSLVerifyClient		None
Contrairement à nginx, apache demande la certificat racine et le certificat du serveur dans un fichier séparé.

Complément

J’avais écrit une doc similaire à destination des administrateurs de grifon dans ma jeunesse. Elle vous permettra peut-être d’éclaircir des points qui ne le sont pas ici.

Supplément

Comme nous avons pu le constater, il existe quelques bémols au HTTPS dûs à la conception de x509. Il existe des moyens de s’en prévenir, mais ils sont encore peu développés. J’ai appelé le DANE et le TLSA : The DNS-Based Authentication of Named Entities (DANE) Transport Layer Security (TLS) Protocol: TLSA.
Ce standard permet de publier un condensat de tout ou partie de sa chaîne de certificats (juste le sien, juste celui de la CA, juste la clé publique, etc.) dans le DNS à condition que la zone soit signée avec DNSSEC. Cela permet de se dispenser de CA car la vérification est directement faite par le client. Le wiki de grifon me semble être un bon point d’entrée pour le moment. J’écrirai un article plus détaillé ici-même plus tard.
Côté client, firefox ne supporte pas nativement la vérification du TLSA, mais les auteur de knot ont écrit un plugin prévu à cet effet, il s’appelle DNSSEC/TLSA Validator.
Avec firefox vous pouvez également vérifier que le certificat du site que vous visitez n’a pas été remplacé inopinément avec Certificate Patrol.

Les confs apache et nginx que je propose plus haut sont prévues pour « juste marcher », mais vous pouvez les durcir en empêchant les ciphers faibles ou en forçant la connexion en HTTPS sur votre site. Attention, ici j’active l’HSTS, entrez à vos risques et périls.
Pour nginx :

server {
	listen		80;
	listen		[::]:80;
	rewrite		^/(.*)$ https://exemple.org/$1 permanent;
	server_name	exemple.org
}
ssl_ciphers "AES128+EECDH:AES128+EDH";
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_stapling on; # Requires nginx >= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7

ssl_dhparam /etc/nginx/ssl/dhparams2048.pem;
Et pour apache :
<VirtualHost *:80>
	RewriteEngine	on
	RewriteCond 	%{HTTPS} !=on
	RewriteRule	.* https://%{SERVER_NAME}%{REQUEST_URI} [NE,R,L]

	ServerName	exemple.org
</VirtualHost>
<IfDefine SSL>
SSLCipherSuite EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH+aRSA+RC4:EECDH:EDH+aRSA:!RC4:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS
SSLProtocol All -SSLv2 -SSLv3
SSLHonorCipherOrder On
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
Header always set X-Frame-Options DENY
Header always set X-Content-Type-Options nosniff
</IfDefine>