Aller au contenu principal

SSL/TLS : certificats, HTTPS et durcissement

· 7 minutes de lecture

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 indispensable 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 :

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 →

En TLS 1.3, le handshake est réduit à 1 round-trip (contre 2 en TLS 1.2), et toutes les étapes sont chiffrées dès le début. 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 les clés de session ne peuvent pas être recalculées.

Types de certificats

TypeCADuréeWildcardUsage
Self-signedSoi-mêmeLibreOuiDev, réseau privé
DV (Domain Validation)Let's Encrypt, ZeroSSL90 joursOui (DNS-01)Sites web standard
OV (Organization Validation)CA payante1-2 ansOuiEntreprises
EV (Extended Validation)CA payante1-2 ansNonBanques, paiement

Let's Encrypt délivre des certificats DV gratuits, renouvelables automatiquement. 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 de votre 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ée
  • cert.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;
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;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:!aNULL:!MD5:!3DES';

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 1.1.1.1 valid=300s;

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 (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 — latence supplémentaire et fuite d'information sur les sites visités.

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.

Certificats autosignés

Pour des services internes inaccessibles publiquement (réseau privé, VPN) :

# 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,nonRepudiation,keyEncipherment,dataEncipherment
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

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 — 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é
  • 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)