Comment HTTPS et TLS fonctionnent réellement : handshake et certificats
Vous ajoutez un certificat SSL, pointez votre domaine vers un serveur, et le cadenas apparaît. La plupart des développeurs s'arrêtent là. Puis un jour, un client API échoue avec UNABLE_TO_VERIFY_LEAF_SIGNATURE, une application mobile rejette votre certificat sur un Android plus ancien, ou un load balancer termine TLS et votre application voit tout en HTTP simple. Soudain, vous devez savoir ce qui se passe réellement entre la connexion TCP et la première requête HTTP.
Cet article détaille le fonctionnement réel de HTTPS et TLS : ce que fait le handshake, comment les certificats sont validés, ce qui a changé dans TLS 1.3, et comment diagnostiquer les erreurs que vous rencontrerez réellement.
Ce que HTTPS ajoute réellement
HTTPS est du HTTP envoyé sur une connexion TLS. TLS offre trois garanties :
- Confidentialité — le trafic est chiffré avec des clés symétriques négociées pendant le handshake.
- Intégrité — chaque enregistrement porte une étiquette d'authentification, de sorte que toute altération est détectée.
- Authentification — le serveur prouve qu'il contrôle la clé privée du certificat correspondant au nom d'hôte que vous avez demandé.
Notez ce qui n'est pas sur cette liste : le client n'est pas authentifié par défaut. TLS authentifie le serveur ; votre application authentifie l'utilisateur.
Le handshake TLS 1.2, étape par étape
TLS 1.2 est encore courant sur les infrastructures plus anciennes et dans de nombreux proxys d'entreprise, il vaut donc la peine de le comprendre. Un handshake complet se déroule ainsi :
- ClientHello — le client envoie les versions TLS supportées, une valeur aléatoire, une liste de suites de chiffrement et (le cas échéant) le nom d'hôte SNI.
- ServerHello — le serveur choisit une version et une suite de chiffrement, renvoie sa propre valeur aléatoire et envoie sa chaîne de certificats.
- Échange de clés — avec ECDHE, le serveur envoie une clé publique éphémère ; le client envoie la sienne. Les deux dérivent le même secret partagé. Le serveur signe la transcription du handshake avec la clé privée de son certificat.
- Finished — les deux côtés envoient un MAC sur la transcription du handshake, confirmant que rien n'a été altéré.
- Données applicatives — les requêtes et réponses HTTP circulent chiffrées.
Le détail crucial est l'échange de clés éphémère. Avec ECDHE, une nouvelle paire de clés est générée à chaque connexion. Même si un attaquant enregistre des années de trafic et vole ensuite la clé privée du serveur, il ne peut pas déchiffrer les sessions passées. Cette propriété s'appelle la confidentialité persistante (forward secrecy), et c'est la principale raison pour laquelle l'échange de clés RSA statique a été déprécié.
Ce que TLS 1.3 a changé
TLS 1.3 simplifie le handshake en un seul aller-retour et supprime entièrement les algorithmes hérités.
| Aspect | TLS 1.2 | TLS 1.3 |
|---|---|---|
| Aller-retours (handshake complet) | 2 | 1 |
| Échange de clés | RSA ou ECDHE | ECDHE uniquement (confidentialité persistante) |
| Suites de chiffrement | Chiffrement, MAC et échange de clés séparés | Suites AEAD uniquement, par ex. TLS_AES_128_GCM_SHA256 |
| RSA statique, CBC, RC4 | Autorisés | Supprimés |
| Reprise de session | ID de session / tickets | PSK avec option 0-RTT |
Dans TLS 1.3, le client devine le groupe d'échange de clés et envoie sa part dans le premier message, de sorte que le serveur peut conclure l'accord de clés immédiatement. Les certificats sont toujours envoyés, mais ils sont désormais chiffrés, ce qui signifie que les observateurs passifs ne peuvent plus voir quel nom d'hôte est servi — seul le SNI dans le ClientHello le révèle, sauf si Encrypted Client Hello est utilisé.
La reprise 0-RTT permet à un client qui revient d'envoyer des données applicatives dès le premier vol. C'est rapide, mais les données 0-RTT sont rejouables, donc ne l'utilisez jamais pour des requêtes non idempotentes comme les paiements.
Certificats et validation de chaîne
Un certificat lie une clé publique à un nom d'hôte. La validation est un parcours de chaîne :
- Le serveur envoie son certificat feuille plus les certificats intermédiaires.
- Le client cherche une racine de confiance dans son magasin, puis vérifie chaque signature en remontant la chaîne.
- Il vérifie le nom d'hôte par rapport au champ Subject Alternative Name (SAN) — le Common Name est ignoré par les clients modernes.
- Il vérifie les dates de validité et le statut de révocation (CRL ou OCSP, souvent agrafé).
La cause la plus fréquente des erreurs unable to verify est un certificat intermédiaire manquant. Les navigateurs contournent parfois cela en mettant en cache les intermédiaires, mais les outils en ligne de commande et les applications mobiles échouent systématiquement. Servez toujours la chaîne complète, la feuille en premier, puis les intermédiaires — jamais la racine.
Débogage pratique avec OpenSSL
Quand quelque chose casse, inspectez ce que le serveur envoie réellement avant de blâmer le client.
# Afficher la chaîne de certificats présentée par le serveur
openssl s_client -connect example.com:443 -servername example.com -showcerts
# Négocier uniquement TLS 1.3 et lister la suite de chiffrement choisie
openssl s_client -connect example.com:443 -tls1_3
# Vérifier l'expiration du certificat en une ligne
echo | openssl s_client -connect example.com:443 2>/dev/null \
| openssl x509 -noout -dates -subject -ext subjectAltNameSi s_client signale verify error:num=21:unable to verify the first certificate, la chaîne est incomplète. S'il signale une incompatibilité de nom d'hôte, vérifiez la liste SAN — les certificats wildcard ne couvrent qu'un seul niveau, donc *.example.com correspond à api.example.com mais pas à a.b.example.com.
Erreurs de configuration courantes
- Contenu mixte — une page HTTPS chargeant des scripts ou images en
http://. Les navigateurs bloquent le contenu mixte actif et peuvent mettre à niveau automatiquement le contenu passif. - TLS terminé au niveau du load balancer — le backend voit du HTTP simple. Transmettez
X-Forwarded-Protoet ne lui faites confiance que depuis des proxys connus, sinon votre application peut générer des URL de redirection incorrectes. - Versions de protocole faibles activées — TLS 1.0 et 1.1 doivent être désactivés. Visez uniquement TLS 1.2 et 1.3.
- Pas de HSTS — sans l'en-tête
Strict-Transport-Security, la première requête peut encore être rétrogradée. Commencez par unmax-agecourt, puis augmentez-le une fois confiant. - Certificats auto-signés en production — acceptables pour les tests internes, mais ils habituent les utilisateurs et les clients à ignorer les avertissements.
Une configuration serveur raisonnable
Pour Nginx, une base moderne ressemble à ceci :
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;Laissez le serveur choisir les suites de chiffrement en TLS 1.3 — la préférence du client y est acceptable car les suites sont toutes fortes. Gardez les tickets de session désactivés sauf si vous faites tourner les clés de ticket, car une clé de ticket compromise peut compromettre la reprise.
FAQ
HTTPS chiffre-t-il le nom d'hôte ?
Pas par défaut. L'extension SNI dans le ClientHello est envoyée en clair pour que le serveur sache quel certificat présenter. Encrypted Client Hello (ECH) le masque, mais l'adoption reste limitée. Le chemin et la chaîne de requête sont toujours chiffrés.
Pourquoi mon certificat fonctionne-t-il dans un navigateur mais échoue dans curl ou une application mobile ?
Presque toujours un certificat intermédiaire manquant. Les navigateurs récupèrent ou mettent souvent en cache les intermédiaires eux-mêmes, tandis que les clients plus stricts exigent que le serveur envoie la chaîne complète. Vérifiez avec openssl s_client -showcerts.
TLS 1.3 est-il toujours plus rapide que TLS 1.2 ?
Pour les nouvelles connexions, oui — un aller-retour au lieu de deux. Pour les connexions reprises, l'écart se réduit, et 0-RTT peut rendre TLS 1.3 encore plus rapide, au prix d'un risque de rejeu sur les requêtes non idempotentes.
Si vous auditez un serveur web après un changement TLS, l'analyse des logs révèle souvent l'impact réel : redirections cassées, pics de 4xx inattendus, ou clients qui continuent d'utiliser HTTP. Vous pouvez déposer un journal d'accès dans l'analyseur de logs Nginx pour voir la répartition des codes de statut et repérer les clients qui n'ont jamais terminé la migration vers HTTPS.