"Le correctif de ce bogue est simple: vérifiez que la longueur du message correspond bien à la longueur de la demande entrante."
Pourquoi avons-nous même le client de signaler la longueur du tout?
Si nous pouvons connaître la longueur de la demande entrante, ne pouvons-nous pas simplement en déduire la longueur du message?
(Il s'agit d'une question de programmation et de conception de protocole.)
Pour TLS dans le but de vérifier la vivacité (maintien en vie), il n'y a aucune raison de:
rrec.length
dans le code OpenSSL - il suffit de soustraire la taille d'en-tête HB fixe de cela),La conception est donc imparfaite et trop compliquée en ce qui concerne le TLS ordinaire. Remarque TLS est le protocole largement utilisé qui nous tient vraiment à cœur, cryptant tout le trafic HTTPS.
Dans le commit OpenSSL vulnérable toutes les requêtes Heartbeat générées ont une petite charge utile fixe (18 octets) et lors du traitement d'une réponse HB reçue, OpenSSL n'en vérifie que les deux premiers octets qui contiennent le numéro de séquence HB. Source: t1_lib.c (contenant tout le code TLS HB) lors de la génération d'un HB (uniquement décrit dans tls1_heartbeat
), il fixe la taille de la charge utile à 18. Le traitement d'une réponse HB dans tls1_process_heartbeat ne fait également un traitement significatif que si la charge utile est exactement 18. Notez le traitement d'une demande dans TLS est la partie vulnérable qui a miné HTTPS.
Contexte
Avant d'arriver à la justification revendiquée, je dois introduire trois concepts: DTLS, PMTU et découverte PMTU qui ne sont pas liés aux contrôles de vivacité, mais traitent de l'autre utilisation proposée pour l'extension Heartbeat. Passez à justification proposée si vous êtes familier.
TLS (cryptage sur TCP) et DTLS (cryptage sur UDP)
TLS régulier ajoute un chiffrement au-dessus de TCP. TCP est un protocole de couche transport qui fournit un flux de transport fiable, ce qui signifie que l'application reçoit un flux de données reconstruit avec tous les paquets présentés à l'application dans l'ordre d'origine une fois que tout est là, même si certains avaient d'attendre un certain temps supplémentaire pour que les paquets soient renvoyés. TCP fournit également un contrôle de congestion (si les paquets sont abandonnés à cause de la congestion, TCP ajustera le débit des paquets) Tous les trafics HTTP, HTTPS, SFTP sont envoyés via TCP.
Datagram TLS (DTLS) est un protocole plus récent qui ajoute un chiffrement au-dessus d'UDP (et des protocoles de datagramme similaires comme DCCP où une application a un contrôle total sur la façon d'envoyer des paquets). Ce sont des protocoles de couche transport qui ne fournissent pas de flux fiables, mais envoient des paquets directement entre les applications client/serveur contrôlées par une application. Avec TCP si un paquet est perdu, il est automatiquement renvoyé et retarde l'envoi de paquets supplémentaires jusqu'à ce que les paquets manquants passent. UDP donne le contrôle du niveau de paquet à l'application, ce qui est souvent souhaitable pour la communication en temps réel comme le chat vidéo bidirectionnel. Si les paquets A, B, C, D ont été envoyés mais que le paquet C a été perdu, cela n'a pas de sens d'attendre que C soit renvoyé avant de montrer le paquet D à l'utilisateur, ce qui entraîne une longue pause.
[~ # ~] pmtu [~ # ~]
Pour DTLS, il est souhaitable de connaître nité de transmission maximale de trajet . Une MTU pour une seule liaison entre les routeurs est la taille de paquet maximale qui peut être envoyée. Différents routeurs et types de liens prennent souvent en charge différents MTU. Le chemin MTU (le plus petit MTU sur le chemin emprunté par vos paquets à travers le réseau) ne sera généralement pas connu à l'avance comme sa propriété du chemin à travers le réseau. Si vous envoyez des datagrammes qui sont plus grands que le PMTU, ils devraient se fragmenter au plus petit point MTU, ce qui n'est pas souhaitable pour plusieurs raisons (les paquets inefficaces et fragmentés peuvent être abandonnés par les pare-feu/NAT, leur confusion pour la couche application et ipv6 par la conception ne fragmentera jamais les paquets). Ainsi, dans le contexte de DTLS, le RFC force les données de votre couche d'enregistrement à tenir dans un seul paquet DTLS (qui est plus petit que le PMTU). (Avec TLS, ces problèmes de PMTU sont traités au niveau TCP; pas la couche application, vous pouvez donc être agnostique pour PMTU).
Découverte PMTU
Il existe des protocoles pour découvrir PMTU - spécifiquement découverte de MTU de chemin de couche de mise en paquet (RFC 4821) . Dans ce contexte, vous sondez le réseau en envoyant des paquets de différentes tailles (configurés pour ne pas fragmenter) et gardez une trace de la limite supérieure et de la limite inférieure du PMTU selon que vos paquets ont traversé le réseau ou non. Ceci est décrit dans RFC4821. Si un paquet de sonde parvient à passer, vous augmentez la limite inférieure, s'il se perd, vous abaissez la limite supérieure jusqu'à ce que la limite supérieure/inférieure soit proche et que vous ayez votre PMTU estimée, qui est utilisée pour définir la taille supérieure de vos paquets DTLS.
Justification alléguée de HB ayant un en-tête de charge utile, un remplissage, un champ de taille allant jusqu'à 2 octets
Le Heartbeats RFC RFC652 indique que vous pouvez utiliser Heartbeats pour la découverte de MTU de chemin pour DTLS:
5.1. Découverte de Path MTU
DTLS effectue la découverte de MTU de chemin comme décrit dans la section 4.1.1.1 de [RFC6347]. Une description détaillée de la façon d'effectuer la découverte de MTU de chemin est donnée dans [RFC4821]. Les paquets de sonde nécessaires sont les messages HeartbeatRequest.
Les applications DTLS doivent estimer le PMTU. Cependant, ceci n'est pas fait par DTLS, c'est fait par l'application utilisant DTLS. En regardant la section citée du DTLS RFC Section 4.1.1.1 du RFC6347 il indique "En général, la philosophie du DTLS est de laisser la découverte PMTU à l'application." Il continue de donner trois mises en garde pour expliquer pourquoi DTLS doit se soucier des PMTU (les applications DTLS doivent soustraire de l'en-tête DTLS pour obtenir la taille PMTU efficace pour les données, DTLS peut devoir communiquer ICMP "Datagram Too Big" à la couche application et DTLS plus tôt dans le RFL DTLS, il déclare que l'enregistrement DTLS DOIT tenir dans un seul datagramme plus petit que le PMTU, donc la découverte/estimation du PMTU doit être effectuée par l'application utilisant DTLS.
Dans la découverte PMTU, il est logique d'avoir un petit champ décrivant la longueur de la charge utile, d'avoir une grande quantité de remplissage arbitraire et d'avoir quelque chose qui résonne, yup a reçu votre demande avec cette taille MTU même si je ne vous renvoie que le numéro de séquence (et pour plus d'efficacité, il est possible de supprimer le remplissage de la réponse). Certes, cela n'a aucun sens si vous décrivez la taille de payload pour permettre à la charge utile d'être plus grande qu'environ ~ 4-32 octets, donc la taille de la charge utile peut être fixée ou décrite par un seul champ d'octets, même si un remplissage arbitrairement long peut être concaténé.
Analyse de la réclamation
L'implémentation et la description d'OpenSSL HB dans le RFC HB ne décrivent ni n'exécutent ce protocole de découverte PMTU. MTU n'est pas présent dans le code. OpenSSL ne fournit qu'un seul mécanisme pour générer une demande HB dans TLS et DTLS, mais il est de taille fixe (charge utile de 18 octets, non configurable).
Au mieux, c'était une conception sérieusement défectueuse (dans TLS) incorporant des fonctionnalités YAGNI, qui a ensuite été mal codée pour faire entièrement confiance à un champ d'en-tête fourni par l'utilisateur sans aucun test d'intégrité. Au pire, les sections PMTU n'étaient qu'une histoire de couverture compliquée pour permettre l'insertion de code vulnérable qui fournit un semblant de justification.
Recherche dans la liste de diffusion IETF TLS
Si vous recherchez la liste de diffusion IETF TLS , vous pouvez trouver des pépites intéressantes. Pourquoi la longueur de la charge utile/du rembourrage est-elle uint16 et pourquoi y a-t-il un rembourrage s'il doit être jeté? découverte PMT . Le même demandeur (Juho Vähä-Herttua) déclare il préférerait fortement la vérification des paquets: lire la longueur de la charge utile, la longueur de remplissage et vérifier qu'elle correspond à la longueur d'enregistrement (en-tête moins) . Aussi Simon Josefsson :
J'ai une légère préoccupation concernant l'autorisation de charges utiles arbitraires. Quelle en est la raison? Il s'ouvre pour un canal latéral dans TLS. Il pourrait également être utilisé abusivement pour envoyer des données non standardisées. De plus, y a-t-il une raison d'autoriser une charge utile de taille arbitraire? À mon avis, les champs payload_length, payload et padding me semblent inutiles.
réponse de Michael Tüxen est largement insuffisant (peut-être qu'une fonctionnalité veut être ajoutée en haut pour dire calculer RTT) et résume avec "Le point ici est que pour l'interopérabilité, peu importe la charge utile, il est seulement important qu'il se reflète. "
A noter également raison du remplissage aléatoire "[nous] randomisons ... les données dans le message de pulsation pour tenter de résoudre tout problème survenant à partir de chiffres faibles ou imparfaits." suivi de la question "Y a-t-il des des articles ou de la documentation sur le chiffrement expliquant comment l'utilisation de données randomisées dans un paquet pourrait résoudre d'éventuels défauts de chiffrement futurs? ", suivi d'un article sur le chiffrement authentifié déterministe. Le la réponse est excellente :
En effet, mais ce n'est pas un mode de cryptage générique comme CBC ou CTR. Il est spécifiquement conçu pour crypter des clés aléatoires et dépend donc de son caractère aléatoire. Les modes de cryptage typiques sont spécifiquement conçus pour empêcher quelqu'un de distinguer un cryptage en texte brut donné d'un cryptage aléatoire.
Maintenant, si l'on souhaite utiliser un canal subliminal dans TLS, l'extension Heartbeat fournit maintenant un canal illimité.
Il est intéressant de noter que ce commentaire n'a jamais été abordé. (À part quelqu'un d'autre commentant "Eh bien, c'est une toute autre question ...").
Je n'ai connaissance d'aucune réponse définitive et "officielle" à ce sujet, mais cela semble s'inscrire dans une tentative de généricité et de cohérence. Dans la norme SSL/TLS , tous les messages suivent des règles de codage normales, en utilisant un langage de présentation spécifique . Aucune partie du protocole "déduit" la longueur de la longueur de l'enregistrement.
Un détail éclairant est le message ClientKeyExchange
: dans SSL 3.0 , avec un échange de clé RSA, le contenu de ce message de prise de contact est le résultat de cryptage "brut" (c'est-à-dire 256 octets si le serveur utilise une clé RSA de 2048 bits, plus l'en-tête du message d'établissement de liaison de 4 octets); dans TLS 1.0 et les versions ultérieures, le résultat du chiffrement est un "vecteur opaque" avec un en-tête supplémentaire de deux octets contenant la longueur, ce qui fait 258 octets, et non 256, et toujours avec un en-tête de message de négociation supplémentaire de 4 octets. Ce changement illustre la volonté des concepteurs standard d'obtenir une régularité stricte avec le moins d'inférence possible sur la longueur des paquets.
Un avantage de ne pas déduire la longueur de la structure externe est qu'il permet beaucoup plus facilement d'inclure des extensions optionnelles par la suite. Cela a été fait avec des messages ClientHello
, par exemple.
Si nous regardons les choses d'un point de vue supérieur , TLS n'est qu'à moitié fini. TLS, en tant que protocole, doit échanger des données structurées; l'expéditeur et le récepteur doivent être capables de coder et de décoder ces données sans ambiguïté, et de préférence sans débordement de tampon non plus. Une stratégie pour cela consiste à faire ce qui suit:
Un mode de réalisation de cette stratégie est ASN.1 . ASN.1 est utilisé, par exemple, dans les certificats X.509; avec un compilateur ASN.1, vous pourriez (théoriquement) coller la syntaxe ASN.1 à partir de la norme X.509 et obtenir un moteur d'encodeur/décodeur complet pour les certificats X.509 ( Je l'ai fait, y compris en écrivant mon propre compilateur ASN.1, et il y a quelques "bizarreries", y compris le fait que les types ASN.1 pour les chaînes et les dates semblent avoir été inventés par un babouin sur LSD; mais le schéma global fonctionne encore).
Un autre mode de réalisation est [~ # ~] xml [~ # ~] , avec Schéma XML comme "langage de présentation": vous poussez le schéma et les données d'entrée dans votre analyseur XML, et si quelque chose d'autre qu'un code d'erreur est produit, alors vous sachez que toutes les données sont conformes au schéma, et vous pouvez les parcourir dans un arbre décodé et structuré. Encore une fois, c'est la théorie, qui est Nice, en ce qui concerne les théories.
Le "langage de présentation" de TLS est un peu court ici: il n'a pas de compilateur. Ce langage ressemble comme un "langage informatique", mais il était destiné à la lecture humaine. Pour cela, cela fonctionne bien; cependant, cela signifie que le développeur devra transcrire cette langue dans un encodeur/décodeur. Le développeur doit veiller à ne pas oublier une étape. Et c'est exactement ce qui s'est produit avec le bug "heartbleed": une étape a été oubliée, ce qui peut avoir des conséquences désastreuses.
Si vous regardez dans RFC6520 (extension de pulsation) il y a un remplissage après la charge utile. La longueur est donc nécessaire pour savoir où se termine la charge utile et où commence le rembourrage. En dehors de cela, je trouve le design trop ingénieux: les deux raisons de cette extension semblent être de rendre PMTU possible (en utilisant des messages de taille différente) et en ayant des battements de cœur pour savoir si l'autre extrémité est toujours en vie ou pour réinitialiser certains compteurs de temporisation dans les états des boîtiers intermédiaires. Il n'était pas nécessaire d'obtenir une charge utile spécifiée par l'utilisateur reflétée dans ces scénarios.
Et du point de vue de la sécurité, ce type de protocoles sur-conçus, où vous ne testez que les éléments dont vous avez vraiment besoin, ne demandent que des problèmes futurs.