TLS 1.3, ESNI, DoH, interception... ce n'est pas si compliqué 😉

⚠️ Le blog a été déplacé vers le site Patrowl, vous serez redirigé automatiquement dans 3 secondes, sinon cliquez sur ce lien : https://patrowl.io/tls-1-3-esni-doh-interception-ce-nest-pas-si-complique-😉/
👍 Vous y retrouverez toutes les nouvelles publications

Bien que finalement peu nouveau, je profite des vacances pour aborder un sujet souvent mal compris : TLS 1.3.

Avec NoLimitSecu, nous avons enregistré un épisode l'année passée (https://www.nolimitsecu.fr/tls-1-3/) mais je trouve qu’il manque quelques éléments que vous trouverez détaillés ci-dessous.

⚠️ C'est un peu hors sujet, mais pensez à la dépréciation de TLS 1.0 et 1.1 par la quasi totalité des acteurs du web entre l'été et l'automne 2020.

SSL/TLS

Le sujet à déjà été traité maintes fois : ce sont les noms des protocoles de chiffrement les plus utilisés et servant en particulier à chiffrer les échanges web (HTTP). SSL (Secure Sockets Layers) étant l’ancienne version et TLS (Transport Layer Security) la nouvelle.

Lors de la connexion d’un client (navigateur) à un site web (www.leroymerlin.fr , www.bricoprive.com…. Oui, je fais des travaux 😉), si l'accès n'est pas déjà chiffré, en général, une redirection est effectuée vers la version chiffrée du site car à terme, les navigateurs ne toléreront plus que les sites chiffrés.

Pour faire très très simple, SSL et TLS proposent surtout deux fonctionnalités :

  • Prouver l'identité d'un service (site web, service mail, API...) avec un mécanisme de certificats. Le serveur envoie au client un certificat signé par une autorité de certification de confiance et l’appareil du client est capable d'en vérifier l’authenticité ;
  • Chiffrer les échanges grâce à différentes suites cryptographiques symétriques comme le standard américain AES (et des mécanismes d'échange de clefs que je ne détaillerai pas ici).

SSL est bourré de failles et n’est donc plus recommandé. De leur côté, les premières versions de TLS sont également vulnérables à diverses attaques et il est plutôt recommandé d’utiliser au minimum TLS 1.2.

Pour les détails, je vous renvoie vers "[Sécurité] Fin annoncée de TLS 1.0 et TLS 1.1".

Server Name Indication / SNI et TLS

Par le passé, il n’était pas possible de fournir plusieurs services différents fonctionnant avec SSL sur une même adresse IP, tout du moins, pas sur le même port. Sur l’adresse IP 1.2.3.4 et le port 443 (port par défaut de HTTPS), il n’était par exemple pas possible d’héberger un site web chiffré https://www.machin.com et un VPN SSL https://accesdistant.machin.com.

En effet, lors de la négociation du chiffrement SSL, le client :

  1. Résolvait l’adresse IP du serveur à partir de son nom de domaine (www.machin.com     résous d'adresse IP 1.2.3.4)
  2. Se connectait à l’adresse IP du serveur (sur le port 443 mais passons ce détail)
  3. Et recevait en retour le certificat signé contenant, entre autres, un champ "Common Name / CN" avec le nom du serveur (www.machine.com) et permettant de prouver son identité.

Le problème était que le serveur n’avait pas moyen de connaître le nom que cherchait à atteindre le client (www.machine.com) et ne pouvait y associer qu’un seul certificat, donc un seul nom de domaine, donc un seul service (dans les faits c’est en partie faux car certains outils comme OpenVPN permettaient d’héberger plusieurs services mais ce n’est pas le sujet, la détection se faisant après la négociation SSL/TLS).

Nous étions alors obligés de dédier une IP par service (car il n’était pas envisageable d’utiliser un autre port que le 443).

Une solution acceptable consistait à utiliser un certificat avec un nom principal (www.machine.com) et contenant aussi un champ "Subject Alternative Names / SAN". Dans ce champ étaient mis les noms des autres services (truc.machin.com, bidule.machin.com...). Mais cette solution ne permettait pas un passage à l’échelle ni d'être dynamique (un certificat est signé pour une longue durée et devoir en générer un nouveau, dès que l’on doit ajouter ou supprimer un service, n’est pas pratique).

Le problème a été rapidement corrigé avec la fonctionnalité « Server Name Indication / SNI », extension du protocole TLS datant de 2003 et permettant au client de spécifier dans sa requête le nom de domaine du service à joindre. Pour les courageux, je vous recommande la lecture de la RFC 3546 https://www.ietf.org/rfc/rfc3546.txt.

Une analogie pourrait être la suivante :

  1. Avant, les postiers n’avaient que l’adresse et pas de nom. Il y’avait qu’un seul destinataire possible par adresse postale ;
  2. Le SAN revient à avoir une conciergerie qui dispose d’une liste des noms des habitants à une adresse postale données et qui récupère le courrier de la main du postier ;
  3. Le SNI équivaudrait à ce que le postier dispose enfin du nom et que chaque habitant puisse lui-même gérer son nom sur sa boite aux lettres.

Depuis, le champ SAN a tout simplement remplacé le champ CN qui n'est supporté que par rétrocompatibilité (https://groups.google.com/a/chromium.org/forum/#!msg/security-dev/IGT2fLJrAeo/csf_1Rh1AwAJ)

Voici un schéma que j’ai repris de CloudFlare :

A présent, la fonctionnalité est largement utilisée, en particulier par les fournisseurs de Cloud, de services SaaS et surtout les CDN (Content Delivery Network comme Cloud Flare, Akamai, CD Network, CloudFront…) qui hébergent des centaines de services (sites) sur des bataillons de serveurs identiques (ou presque).

Encrypt-then-MAC, MAC-then-Encrypt, Je-comprends-riennnnnnn...

En général, lors d’échanges de messages chiffrés, afin de protéger l’intégrité du message (qu’il n’a pas été modifié), le message chiffré est accompagné de données permettant de l’authentifier. Il ne s’agit pas ici de l’authentification au sens d’un utilisateur (mot de passe, authentification forte…) mais d’authentifier le message, c’est-à-dire de prouver qu’il n’a pas été modifié, que son intégrité n’a pas été altérée.

En général (ça fait 2 fois, peut-on dire « en généraux » ?😆 ), il s’agit d’un condensat généré à partir du message (chiffré ou pas, nous verrons cela après), de données techniques et parfois de données aléatoires afin de rendre impossible le rejeu (« Nonce » en anglais ou parfois noté IV pour « InitializationVector»).

Le terme employé est « MAC » pour Message Authentication Code.

Il existe alors plusieurs moyen de réaliser ce traitement d’authentification (c'est détaillé juste après):

  • Je ne fais rien 👍 ;
  • Je génère le condensat du message en clair, puis je chiffre le tout : MAC-then-Encrypt  / Authenticate then Encrypt / MtE
  • Je génère le condensat du message en clair, chiffre le message et envoie les deux (message chiffré + condensat) : Encrypt-and-MAC / Encrypt and Authenticate / E&M
  • Je chiffre mon message, génère son condensat et envoie les deux (message chiffré + condensat du chiffré) : Encrypt-then-MAC / Encrypt then Authenticate / EtM

Voici les exemples les plus communément cités :

  • IPSec fait Encrypt-then-MAC
  • SSL (et non pas TLS) fait MAC-then-Encrypt
  • SSH fait Encrypt-and-MAC

Je ne fais rien

Je ne vais pas m’attarder sur le sujet, c’est le pire des cas car les messages peuvent être modifiés et/ou rejoués, sans possibilité de le savoir, c’est de la merde 😜 : https://www.youtube.com/watch?v=PuCRGsC9XhU

MAC-then-Encrypt

Supposons un message en clair « msg » (ou Plaintext) et un nombre aléatoire « random » (ou Nonce).

MAC-then-Encryption revient à envoyer (attention aux parenthèses) : Encrypt(msg concaténé avec Hash(random + msg))

Je trouve les schémas de Wikipedia perturbant du fait de l’utilisation confusante de Key1 et Key2 mais cela permet d’illustrer :

https://en.wikipedia.org/wiki/Authenticated_encryption#Encrypt-then-MAC_(EtM)

Cela permet d’assurer l’intégrité du texte en clair, mais pas de celle du chiffré. Sans déchiffrer, il n’est pas possible de savoir si le message a été modifié.

Ce mode est susceptible d’être vulnérable à plusieurs attaques http://cseweb.ucsd.edu/~mihir/papers/oem.htmlet https://eprint.iacr.org/2001/045.

Encrypt-and-MAC

Encrypt-and-MAC revient à envoyer :  Encrypt(msg) concaténé avec Hash(random + msg)

https://en.wikipedia.org/wiki/Authenticated_encryption#Encrypt-and-MAC_(E&M)

L’intégrité du texte chiffré n’est pas assurée, ce qui permet de lancer des attaques en choisissant le chiffré. Par contre, l’intégrité du texte clair est assurée, mais il est nécessaire de déchiffrer, ce qui peut générer des erreurs et être exploité.

Enfin, suivant les implémentations, si dans les données « random » il n’y a pas de compteur, il est possible de réaliser des attaques en clair connu sur le condensat.

Encrypt-then-MAC

Encrypt-then-MAC revient à envoyer :  Encrypt(msg) concaténé avec Hash(random + Encrypt(msg))

https://en.wikipedia.org/wiki/Authenticated_encryption#Encrypt-then-MAC_(EtM)

Ce mode permet d’en garantir l’intégrité du texte chiffré ainsi mais si l’algorithme de condensat est cassé ou affaibli, il devient alors possible de réaliser des attaques sur « random » de type « tampering » (que je ne sais pas traduire simplement en français, en très simplifié, il s’agit de modifier le contenu afin de tenter de retrouver la clef de chiffrement).

Cela reste le mode le plus robuste à ce jour.

TLS 1.3

La dernière version sûre de TLS étant la 1.2, sans rentrer dans les détails, il serait aisé de croise que TLS 1.3 n’est qu’une simple évolution alors qu’il s’agit en fait d’une réelle rupture et il aurait été préférable de l’appeler TLS 2.0 mais le nommage est quelque chose de compliqué (cf. la vidéo à la fin, à 38:54).

Cette version apporte plusieurs changements notables :

  • Meilleure vitesse lors de la négociation, en réduisant les allers-retours entre le client et le serveur à un seul échanges (contre deux précédemment) nommés « RTT     » (Round Trip Time Resumption     pour aller/retour et où est le dernier R me demanderez-vous ? Bonne question !). Si le client s'est déjà connecté au serveur, alors nous sommes dans un cas d'optimisation nommé "0-RTT" (zéro aller/retour) permettant de reprendre une connexion passée;
  • Désactivation de toutes les suites cryptographiques faibles     ou à risque, encore supportées par TLS 1.2. Avec TLS 1.3, vous êtes obligé d'utiliser des algorithmes forts, que ce soit pour le chiffrement, les condensats (hash) ou les protocoles de chiffrements bloc par bloc ;
  • Fin des clefs statiques lors des échanges de clefs par RSA  et Diffie-Hellman. A présent, le "Forward     Secrecy" est obligatoire, c'est à dire que les clefs changent tout au long des échanges et qu'il n'est plus possible d'enregistrer le trafic, trouver la clef et déchiffrer de trafic après coup;
  • Des alternatives cryptographiques aux recommandations du NIST et de la NSA, apportant plus de confiance. Ceci grâce/à cause de Dual_EC_DRBG, l'algorithme de génération de nombres pseudos aléatoire, compromis par la NSA, normalisé dans FIPS 140-2 et diffusé très largement (cf. "[Sécurité] NSA et PRNG", "[FUN] La backdoor de la NSA dans OpenSSL n'a jamais marché ( FIPS 140-2 )", "[Crypto] Le NIST supprime Dual EC DRBG NSA) de son guide", "[Sécurité] Dual EC DRBG toute l'histoire / NSA"). La courbe elliptique 25519 est supportée et présente une alternative libre aux courbes du NIST et de la NSA;
  • De la même manière, l'algorithme     de chiffrement symétrique libre ChaCha20 et l'asymétrique EdDSA sont supportés, pour fournir des alternatives à deux du NIST et de la NSA;
  • Obligation d’authentifier les messages     chiffrés avec en particulier 2 modes : GCM (Galois Counter Mode) et CCM (Counter with CBC-MAC). Pour les détails, je vous renvoie vers le schéma de Wikipedia     qui est plutôt bien fait : https://fr.wikipedia.org/wiki/Galois/Counter_Mode     ;
  • Et bien d'autres ajustements : optimisations des échanges, réductions des quantités de données échangées en clair...

Une autre différence longtemps débattue est la possibilité de réaliser une interception des flux en les déchiffrant. C'est tout à fait possible avec TLS 1.3 et un protocole dédié à cela a même été ajouté : ETLS (Enterprise TLS), parfois appelé "TLS interception for grostocards".

https://www.etsi.org/deliver/etsi_ts/103500_103599/10352303/01.01.01_60/ts_10352303v010101p.pdf#page=8

Ce protocole, ou option de TLS 1.3, utilise, entre autre, une clef Diffie-Hellman statique et autorise un tiers à récupérer le trafic chiffré et une copie de cette clef. Pour faire simple, cela désactive le "Forward Secrecy". Pour le faire encore plus simple : c'est de la crotte 💩😋.

Si vous souhaitez faire de l'interception SSL/TLS propre, normale, respectueuse de l'environnement et de l’intelligence humaine, il suffit de faire comme avant : faire passer tout le trafic par un proxy disposant d'une autorité de certification qui signe les certificats dynamiquement (tous les proxy savent faire, que ce soit du Bluecoat, de l'Ironport ou du Zscaler) et déployer la partie publique de cette autorité de certification dans le magasin de certificat de vos postes de travail, serveurs (qui ne doivent pas accéder directement à internet en mode fête du slip), vos smartphones... en tant qu'autorité racine de confiance.

Voici une documentation de Symantec sur l'interception "éthique" 😇 : https://www.symantec.com/content/dam/symantec/docs/other-resources/responsibly-intercepting-tls-and-the-impact-of-tls-1.3-en.pdf

Par contre, vous ne pourrez plus mettre d'IDS/IPS sur votre infrastructure exposée à Internet avec une réplication du trafic (TAP) pour le déchiffrer sans être en coupure (sauf à utiliser eTLS mais je ne vous raconte pas l'usine à gaz). Franchement, l'intérêt d'un IDS/IPS dans ce cas-là me parait très limité si vous respectez bien les bonnes pratiques (mise à jour, cloisonnement, audits...) et si vous disposez par exemple d'un WAF ou équivalent portant le chiffrement (ou s'il est porté avant, comme par exemple avec un CDN).

TLS 1.3 est donc un très bon protocole mais présentait encore deux faiblesses :

  • Pour se connecter à un service, il faut résoudre le nom de domaine, ce qui est réalisé avec le protocole DNS, qui n’est pas chiffré (Non, DNSSEC ne permet pas de chiffrer DNS mais uniquement de s’assurer que l’intégrité de la réponse n’a pas été altérée);
  • Le nom de domaine que l’on chercher à joindre, situé dans le champ SNI de TLS n’est pas chiffré, car présent dans la première requête du client, avant l’établissement d’un canal chiffré.

Cette seule information (le nom de domaine) suffit à réaliser de l’espionnage sur un réseau WiFi ou à l’échelle d’un état, ainsi qu’à censurer. Heureusement, ESNI permet de palier à cette problématique, que je vous détaille juste après.

Trusted Recursive Resolver / TRR

Avant de parler de DNS over HTTPS, il faut juste introduire une notion finalement simples : les résolveurs DNS de confiance (les résolveurs qui mentent sont malheureusement fréquents, sans forcément parler de piratage). En gros, plusieurs éditeurs de navigateur se sont associés avec des entreprises comme CloudFlarepour qu’ils créent des services de résolution de noms de domaine avec la garantie qu’ils ne modifieront pas les réponses. Ainsi le navigateur, qui précédemment utilisait le serveur DNS configuré dans le système d’exploitation, peut s’en passer et interroger directement les services de résolution DNS de confiance.

Cela revient tout simplement à une liste blanche de serveurs de confiance qui font office de relai des requêtes DNS. Ce sont eux ensuite qui relaient la requête DNS à qui de droit.

Dans les faits… il y’en a deux 😉 : https://mozilla.cloudflare-dns.com/dns-queryet https://dns.google.com/experimental  (https://wiki.mozilla.org/Trusted_Recursive_Resolver).

Je passe rapidement sur le fait que ces serveurs de confiance permettent la géolocalisation (partielle), utile aux CDN et, dans l’idéal, c’est le serveur le plus proche de l’utilisateur qui est utilisé (avec un fonctionnement classique de type CDN).

DNS over HTTPS / DoH

Ce protocole, décrit par la RFC 8484, nécessite le support d’HTTP/2 et de ses flux (streams) afin de ne pas trop perdre en temps de réponse.

Il s’agit d’une encapsulation de DNS dans de l’HTTP sur TLS. C’est donc bien le contenu d’une requête DNS classique qui est envoyée en HTTP, encodée en base64 dans le cas de l’emploi de requêtes de type GET et sans encodage dans le cas de requêtes POST.

Voici un outil en Perl (désolé) faisant ce type de requête : https://github.com/bagder/dns2doh

Sinon, il y’a CURL (en version récente) :

~# curl --doh-url https://dns-server.example.com

Vous me direz qu’afin de pouvoir réaliser cette résolution de noms de domaine sur HTTPS, il faut tout d’abord réaliser une requête DNS classique afin d’obtenir l’adresse IP correspondant au serveur TRR, ce qui n’est pas chiffré et reviendrait au problème de l’œuf et de la poule mais il ne s’agit finalement que de la résolution du serveur DNS, ce qui ne laisse pas fuiter d’information sur vos requêtes DNS réelles. Pour avoir une solution parfaite, il faudrait mettre en dur dans le code les adresses IP des serveurs ce qui semble infaisable.

Encrypted Server Name Indication / ESNI

Pour tout problème, il existe une solution, c’est donc à nouveau une extension de TLS qui a résolu le problème des noms de domaine en clair lors de la connexion à un service : Encrypted Server Name Indication.

L'hébergeur ou l'entreprise qui souhaite utiliser ESNI doit disposer d'un enregistrement DNS contenant une structure de données avec en particulier une clef publique. De cette clef publique est dérivée une clef symétrique servant à chiffrer le nom de domaine dans la requête.

A noter que ce potentiel futur standard est encore à l'état de brouillon :  https://datatracker.ietf.org/doc/draft-ietf-tls-esni/?include_text=1

Voici par exemple l'enregistrement DNS pour CloudFlare (la structure de données en rouge est encore en base64) :

~# dig TXT _esni.cloudflare.org +short
"/wH7nPYtACQAHQAgGFV9e448B0Nkg0dLwKX3cMwHMcJ4PX29THIg/kguXXEAAhMBAQQAAAAAXWlIAAAAAABdcTEAAAA="

Pour les détails, j'ai trouvé peu de codes sources détaillant le découpage de la structure, en voici un exemple en python : https://gist.githubusercontent.com/mosajjal/c088d03225287115a2e1fffef82ed25b/raw/fc37b51ac4067975a1c7e70dc0fb61a5781b078b/esni_creator.py

Étant donné que le but est de dissimuler le nom du site visité, il est fortement recommandé d'utiliser une clef pour de nombreux services et non pas une par service. Vous l'aurez compris, cette fonctionnalité est surtout utile et prônée par les gros hébergeurs et CDN comme CloudFront. Voici d'ailleurs un article de CloudFlare sur le sujet : https://blog.cloudflare.com/esni/

TRR, DoH, ESNI... Tout ceci complexifie grandement la cinématique de connexion à un site web et repose sur peu d’acteurs, mais il est heureusement toujours possible de fonctionner avec l’ancien modèle 😀.

0-RTT et le rejeu de paquet

Du fait de l'optimisation de l'échange TLS, il est possible de rejouer le premier paquet TLS envoyé, à condition que l'attaquant soit en capacité d'intercepté le trafic (WiFi...) :

  • Côté client, le navigateur va signaler une erreur réseau, transparente pour l'utilisateur car gérée par son navigateur qui rejouera la requête;
  • Côté serveur, cette requête spécifique sera vue deux fois.

Dans les faits, il est possible de rejouer n'importe quel paquet TLS : https://vnhacker.blogspot.com/2015/12/bad-life-advice-never-give-up-replay.html

Les risques sont limités car les cas d'exploitation sont très rares et la plupart des applications web ajoutent des identifiants uniques non rejouable pour les requêtes sensibles comme les virements ou paiements.

Le risque n'étant pas nul, certains CDN comme CloudFlare ne répondent qu'à certaines requêtes 0-RTT comme les GET sans paramètre et ajoutent un entête HTTP spécifique : "Cf-0rtt-Unique: valeur-unique-liant-la-clef-de-session-et-la-négo-tls". Par contre, pour les autres paquets, rien 😱.

Conclusion

Vous l'aurez compris, TLS 1.3 vient corriger de nombreuses faiblesses des versions précédentes et marque un changement important avec l'abandon d'une rétrocompatibilité devenue gênante.

Dans l'idéal, il faudrait autoriser uniquement TLS 1.3 sur tous vos services mais afin d'éviter de bloquer certains clients ou outils non compatibles, il est préférable de continuer à autoriser TLS 1.2, voire 1.1 dans certains cas.

Si vous disposez de 47 minutes, je vous invite à regarder cette présentation en anglais à la conférence SSTIC de 2017 : https://www.sstic.org/2017/presentation/2017_invite_2/ et les slides https://lab.dsst.io/slides/33c3/slides/8348.pdf