SSL/TLS : certificats, HTTPS et durcissement
TLS chiffre les communications entre client et serveur, vérifie l'identité du serveur via un certificat signé par une autorité de certification (CA), et garantit l'intégrité des données échangées. Comprendre la chaîne de certification et le handshake TLS est nécessaire pour configurer correctement HTTPS et diagnostiquer les erreurs de certificat en production.
La chaîne de certification (PKI)
La confiance dans un certificat repose sur une hiérarchie : le navigateur fait confiance à des CA racines (Root CA) préinstallées dans son store. Ces CA racines signent des CA intermédiaires, qui signent les certificats des serveurs.
Root CA (store de confiance du navigateur)
└── CA intermédiaire (Intermediate CA)
└── Certificat serveur (example.com)
Quand un serveur présente son certificat, il présente aussi la chaîne complète (fullchain.pem) : certificat serveur + CA(s) intermédiaire(s). Sans la chaîne complète, certains clients ne peuvent pas vérifier la signature et refusent la connexion.
Trois éléments constituent une configuration TLS côté serveur :
- Clé privée : générée localement, ne quitte jamais le serveur. Sert à prouver la possession du certificat.
- CSR (Certificate Signing Request) : contient la clé publique et les informations du domaine. Envoyé à une CA pour obtenir un certificat signé.
- Certificat : délivré par la CA, présenté au client. Contient la clé publique, le nom de domaine (CN + SAN), la durée de validité et la signature de la CA.
Le handshake TLS
À chaque nouvelle connexion, client et serveur négocient les paramètres de la session via un handshake. Le schéma suivant correspond au handshake historique de TLS 1.2 avec échange de clés RSA :
Client Serveur
→ ClientHello (versions TLS, cipher suites, random)
← ServerHello (version choisie, random, session ID)
← Certificate (certificat + chaîne)
← ServerHelloDone
→ ClientKeyExchange (pre-master secret chiffré avec la clé publique du serveur)
→ ChangeCipherSpec
→ Finished (hash de la session)
← ChangeCipherSpec
← Finished
← données chiffrées →
Dans ce mode, le secret de session est chiffré avec la clé publique du certificat : quiconque enregistre le trafic et obtient plus tard la clé privée du serveur peut déchiffrer toutes les sessions passées. Les suites ECDHE de TLS 1.2 remplacent ClientKeyExchange chiffré par un échange Diffie-Hellman éphémère (message ServerKeyExchange signé par la clé du serveur), ce qui supprime ce risque.
TLS 1.3 ne conserve que les échanges éphémères et réduit le handshake à un aller-retour (contre deux en TLS 1.2) : le client envoie sa part de clé Diffie-Hellman dès le ClientHello, en anticipant le groupe que le serveur choisira.
Client Serveur
→ ClientHello (+ key_share, supported_versions, SNI)
← ServerHello (+ key_share)
← {EncryptedExtensions}
← {Certificate}
← {CertificateVerify}
← {Finished}
→ {Finished}
← données chiffrées →
{ } : messages chiffrés avec les clés dérivées du handshake
Seuls ClientHello et ServerHello circulent en clair : le certificat du serveur, visible par tout observateur en TLS 1.2, est chiffré en TLS 1.3. Le nom de domaine demandé (SNI) reste en revanche lisible dans le ClientHello, sauf déploiement d'Encrypted Client Hello (ECH). Les algorithmes à clé éphémère (ECDHE) garantissent la perfect forward secrecy : si la clé privée du serveur est compromise ultérieurement, les sessions passées restent protégées, car la clé privée ne sert qu'à signer le handshake et non à transporter le secret de session.
Types de certificats
| Type | CA | Durée | Wildcard | Usage |
|---|---|---|---|---|
| Self-signed | Soi-même | Libre | Oui | Dev, réseau privé |
| DV (Domain Validation) | Let's Encrypt, ZeroSSL | 90 jours (Let's Encrypt) | Oui (DNS-01) | Sites web standard |
| OV (Organization Validation) | CA commerciale | 200 jours max. | Oui | Entreprises |
| EV (Extended Validation) | CA commerciale | 200 jours max. | Non | Banques, paiement |
La durée maximale des certificats publics est fixée par le CA/Browser Forum : 398 jours depuis 2020, puis 200 jours pour les certificats émis à partir du 15 mars 2026, avec un calendrier qui la ramène à 100 jours en 2027 puis 47 jours en 2029. Cette réduction rend le renouvellement automatisé (ACME) nécessaire, quel que soit le type de certificat. Let's Encrypt délivre des certificats DV gratuits, renouvelables automatiquement, et a annoncé une réduction progressive de leur durée de 90 à 45 jours. Les certificats EV ne donnent plus l'indicateur "cadenas vert avec le nom de l'organisation" dans les navigateurs modernes ; leur avantage principal est désormais la confiance organisationnelle pour les partenaires B2B.
Obtenir un certificat Let's Encrypt avec Certbot
Certbot automatise l'obtention et le renouvellement des certificats Let's Encrypt.
# Installation (Debian/Ubuntu)
sudo apt update && sudo apt install -y certbot python3-certbot-nginx
# Challenge HTTP-01 : Let's Encrypt contacte le port 80 du serveur
sudo certbot --nginx -d example.com -d www.example.com
# Sans Nginx (mode standalone, libère le port 80 temporairement)
sudo certbot certonly --standalone -d example.com
Certbot installe les fichiers dans /etc/letsencrypt/live/<domain>/ :
fullchain.pem: certificat + chaîne intermédiaire (à utiliser dans Nginx)privkey.pem: clé privéecert.pem: certificat seul (sans la chaîne)
# Vérifier le timer de renouvellement automatique
sudo systemctl list-timers | grep certbot
# Tester le renouvellement sans l'appliquer
sudo certbot renew --dry-run
# Forcer le renouvellement immédiat
sudo certbot renew --force-renewal
Challenge DNS-01 et certificats wildcard
Le challenge HTTP-01 nécessite que le domaine soit accessible publiquement sur le port 80. Le challenge DNS-01 prouve la possession du domaine via un enregistrement TXT DNS. Il fonctionne derrière un NAT, sur des serveurs non exposés, et est le seul moyen d'obtenir des wildcard (*.example.com).
# Certbot avec DNS-01 manuel (interactif, pour test)
sudo certbot certonly --manual --preferred-challenges dns \
-d "*.example.com" -d "example.com"
# Certbot va demander d'ajouter un enregistrement TXT :
# _acme-challenge.example.com → "valeur_générée"
Pour l'automatisation, utiliser un plugin de provider DNS :
# Avec Cloudflare
sudo apt install python3-certbot-dns-cloudflare
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d "*.example.com" -d "example.com"
# /etc/letsencrypt/cloudflare.ini
dns_cloudflare_api_token = your_api_token
Configuration Nginx pour HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on; # Nginx >= 1.25.1
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Suites TLS 1.2 uniquement (celles de TLS 1.3 sont gérées séparément par OpenSSL)
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
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=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header Referrer-Policy "strict-origin-when-cross-origin";
location / {
proxy_pass http://127.0.0.1:8000;
}
}
OCSP Stapling : le serveur récupère et met en cache la réponse OCSP (statut de révocation du certificat) auprès de la CA, puis la fournit directement au client pendant le handshake TLS. Sans stapling, le client doit contacter lui-même la CA, d'où une latence supplémentaire et une fuite d'information sur les sites visités. Le mécanisme se configure avec ssl_stapling on;, ssl_stapling_verify on; et une directive resolver pour joindre le répondeur OCSP. Il ne s'applique qu'aux certificats qui contiennent une URL OCSP : Let's Encrypt a abandonné OCSP en 2025 au profit des listes de révocation (CRL), ses certificats n'en contiennent plus et la configuration ci-dessus n'active donc pas le stapling. Il reste pertinent pour les CA commerciales qui maintiennent un répondeur OCSP.
HSTS (Strict-Transport-Security) : force le navigateur à utiliser HTTPS pour les visites futures pendant max-age secondes. Activer includeSubDomains uniquement après avoir vérifié que tous les sous-domaines supportent HTTPS : la directive s'applique immédiatement et un sous-domaine sans HTTPS devient inaccessible. Le mot-clé preload n'a d'effet qu'après inscription du domaine sur la liste hstspreload.org, intégrée en dur dans les navigateurs : le retrait de cette liste prend plusieurs mois, l'inscription est donc à réserver aux domaines dont tous les sous-domaines sont durablement servis en HTTPS.
Certificats autosignés
Pour des services internes inaccessibles publiquement (réseau privé, VPN), aucune CA publique ne peut valider le domaine. Plutôt qu'un certificat autosigné par serveur, qu'il faudrait approuver un par un sur chaque client, la méthode courante consiste à créer une CA interne dont seul le certificat racine est distribué aux clients :
# Générer une CA locale
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \
-out ca.crt -subj "/CN=Internal CA"
# Générer un certificat signé par cette CA
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr \
-subj "/CN=internal.example.local"
# Fichier d'extensions (SAN obligatoire pour les navigateurs modernes)
cat > server.ext << EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage=digitalSignature,keyEncipherment
extendedKeyUsage=serverAuth
subjectAltName=DNS:internal.example.local,IP:192.168.1.10
EOF
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out server.crt -days 365 -sha256 -extfile server.ext
Les navigateurs ignorent le champ CN et ne vérifient que l'extension subjectAltName : un certificat sans SAN est rejeté même si le CN correspond au domaine. Pour que les clients acceptent le certificat sans avertissement, distribuer ca.crt au store de confiance du système :
# Linux (Debian/Ubuntu)
sudo cp ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
# macOS
sudo security add-trusted-cert -d -r trustRoot \
-k /Library/Keychains/System.keychain ca.crt
mTLS (Mutual TLS)
En TLS standard, seul le serveur présente un certificat. En mTLS, le client présente aussi un certificat, et le serveur vérifie l'identité du client avant d'accepter la connexion. C'est le mécanisme d'authentification utilisé dans les service meshes (Istio, Linkerd) pour sécuriser les communications entre microservices.
server {
listen 443 ssl;
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# Exiger un certificat client signé par cette CA
ssl_client_certificate /etc/nginx/certs/client-ca.crt;
ssl_verify_client on;
location / {
# L'identité du client est disponible dans $ssl_client_s_dn
proxy_set_header X-Client-Cert-Subject $ssl_client_s_dn;
proxy_pass http://127.0.0.1:8000;
}
}
Vérifications pratiques
# Certificat présenté par un serveur (dates, CN, SAN, chaîne)
openssl s_client -connect example.com:443 -servername example.com < /dev/null \
| openssl x509 -noout -text
# Dates de validité uniquement
echo | openssl s_client -connect example.com:443 2>/dev/null \
| openssl x509 -noout -dates
# Tester une configuration Nginx
curl -v https://example.com 2>&1 | grep -E "SSL|TLS|expire|subject"
# Vérifier que HSTS est présent
curl -I https://example.com | grep -i strict
Pour un audit complet de la configuration TLS (ciphers, protocoles, vulnérabilités connues), SSL Labs est la référence.
Checklist de durcissement
- Forcer TLS 1.2+ (désactiver TLS 1.0 et 1.1)
- Activer TLS 1.3 (
ssl_protocols TLSv1.2 TLSv1.3) - Désactiver les session tickets (
ssl_session_tickets off) pour la forward secrecy - Activer OCSP stapling (
ssl_stapling on) avec un resolver configuré, si la CA fournit encore un répondeur OCSP - Configurer HSTS avec une durée longue (63072000s = 2 ans)
- Rediriger tout le trafic HTTP vers HTTPS (code 301)
- Ajouter
X-Content-Type-Options,X-Frame-Options,Referrer-Policy - Automatiser le renouvellement et tester avec
certbot renew --dry-run - Vérifier la configuration avec SSL Labs (grade A minimum)
Conclusion
La sécurité d'une configuration HTTPS repose sur trois éléments : une chaîne de certification complète, des protocoles et suites limités aux échanges éphémères, et un renouvellement automatisé, rendu nécessaire par la réduction de la durée des certificats. Le challenge DNS-01 étend l'obtention automatique aux certificats wildcard et aux services non exposés sur Internet. Les articles Nginx et Traefik détaillent la terminaison TLS dans un reverse proxy, le second avec l'obtention des certificats par ACME intégrée.