Faire passer un écouteur en TLS 1.3 change ce qu’un observateur peut lire, mais pas autant que le numéro de version le laisse croire. Le ClientHello circule encore avant toute négociation de chiffrement, et dans une connexion ordinaire le nom de serveur demandé s’y trouve en clair. Encrypted Client Hello est le mécanisme qui le cache, et c’est un déploiement distinct avec ses propres prérequis. Exécutez ces quatre étapes avant d’écrire une politique qui suppose l’un ou l’autre comportement.

Ce que la poignée de main expose réellement

TLS 1.3 chiffre le certificat du serveur et la majeure partie de la poignée de main qui suit le ServerHello. Tout ce que le client envoie en premier échappe à cette protection, parce que les clés n’existent pas encore. L’indication du nom de serveur fait partie de ce premier vol, et c’est pourquoi un observateur passif peut encore la lire sans rien casser.

La spécification ECH, RFC 9849, change cela en chiffrant un ClientHello interne pour un serveur coopératif. La poignée de main externe subsiste et porte un nom public que le réseau peut voir, si bien que le déploiement a toujours besoin d’une identité externe qui a du sens pour les intermédiaires. Ce nom externe est la partie que les gens manquent lorsqu’ils attendent que la connexion devienne anonyme. C’est un nom de couverture, pas un nom caché, et il doit se résoudre et s’authentifier comme n’importe quel autre.

Rien de tout cela ne cache le chemin d’acheminement. Les adresses source et destination, les ports, la taille des paquets et les temporisations restent observables, et celles-ci en disent souvent plus sur qui parle que le nom. La résolution de noms et les relations au niveau applicatif sont encore une autre couche, chacune avec sa propre visibilité.

Lire un ClientHello au lieu de supposer

La version dans le ServerHello ne vous dit pas ce que le client a envoyé. Regardez le premier vol directement, depuis une position qui représente l’observateur qui vous intéresse.

openssl s_client -connect app.plant.example:443 -servername app.plant.example -tls1_3 </dev/null> 2>/dev/null | head -20
sudo tcpdump -ni eth0 -c 40 'tcp port 443' -w /tmp/tls.pcap
tshark -r /tmp/tls.pcap -Y 'tls.handshake.extensions_server_name' -T fields -e tls.handshake.extensions_server_name

La première commande effectue une poignée de main et affiche les paramètres négociés, y compris la version du protocole et le nom que vous avez demandé. La deuxième capture les premiers paquets d’une vraie connexion, et la troisième extrait le champ SNI de cette capture. Si le champ est rempli, l’observateur que vous avez imité peut le lire.

Capturez séparément côté client et sur le chemin lorsque les deux ne concordent pas, car une capture locale peut montrer un nom qu’un middlebox réécrit ou abandonne ensuite. Capturez aussi depuis un hôte qui ne termine pas TLS lui-même, puisqu’un proxy terminant voit le clair par conception et ne prouve rien sur ce que le réseau voit.

Traiter une extension ECH comme une affirmation, pas une preuve

Voir une extension ECH dans un ClientHello ne signifie pas que la connexion a utilisé ECH. Les clients envoient GREASE ECH lorsqu’ils n’ont pas de configuration utilisable, précisément pour que leurs poignées de main se ressemblent. L’extension est présente, le hello interne est un leurre, et le nom réel reste lisible.

dig +short HTTPS app.plant.example
openssl s_client -connect app.plant.example:443 -tls1_3 -status </dev/null> 2>/dev/null | grep -i -E 'ech|status_request'

La première commande indique si l’origine publie un enregistrement de ressource HTTPS avec un paramètre de configuration ECH, ce qui est la façon dont les clients apprennent la clé. La seconde cherche des informations d’état de la poignée de main, même si le message de succès d’un client est la preuve la plus solide. L’acceptation est ce qui compte : un serveur qui ne détient pas la clé correspondante ne peut pas déchiffrer le hello interne, et la connexion retombe.

Vérifiez aussi le client, car la prise en charge par les navigateurs et la configuration varient selon la version et la plateforme. Une politique fondée sur « les navigateurs modernes utilisent ECH » est une supposition sur un parc, pas un fait sur votre parc. Inventoriez les versions qui se connectent réellement, puis testez-en une de chaque. La même page qui fonctionne depuis un navigateur de bureau à jour peut ne plus rien cacher à un client plus ancien sur le même réseau.

Écrire le périmètre de l’observateur dans la politique

Précisez où se situe l’observateur et ce qui est visible à cet endroit avant de décider sur quoi un contrôle peut s’appuyer. La connexion d’un navigateur à un site, un transport VPN externe et un proxy terminant TLS exposent chacun un ensemble de faits différent, et une règle écrite pour une position se transpose rarement proprement à une autre.

Si un contrôle dépend de la lecture du SNI, testez comment les clients déployés se comportent lorsque ECH est disponible et lorsqu’il ne l’est pas. Consignez ensuite la conséquence de compatibilité, car un filtre qui cesse silencieusement de correspondre est pire qu’un filtre qui n’a jamais été déployé.

Enfin, gardez l’incertitude dans le dossier. Remplacer une ancienne affirmation universelle par une plus récente laisse le même vide sous un autre nom de protocole, et le prochain relecteur y croira tout aussi fermement.

Articles similaires