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>