Résumé

  • Le masquage WebSocket ne protège aucun secret : sa clé de quatre octets circule dans la trame, mais son imprévisibilité prive l'application du choix préalable de l'image binaire envoyée.
  • La règle répond à une panne réelle d'intermédiaires : les trames client doivent être masquées avec une clé neuve, celles du serveur ne doivent pas l'être, et l'application ne peut plus modifier une trame dès que son émission a commencé.

Les premières trames faisaient confiance à leurs limites

Le projet IETF de mai 2010 proposait un canal bidirectionnel après une ouverture en HTTP. Son format était volontairement réduit : 0x00 ouvrait une trame texte, 0xff la fermait, tandis qu'une autre forme annonçait une longueur. Pour deux extrémités conformes, cette grammaire suffisait à séparer les messages.

Elle ne commandait pourtant pas les équipements déjà installés sur le trajet. Un proxy d'interception pouvait laisser passer Upgrade puis continuer à chercher une requête HTTP dans les octets suivants. WebSocket avait changé de langage ; le parseur intermédiaire, lui, se croyait encore en HTTP.

La menace ne venait donc pas nécessairement d'un proxy hostile. Il suffisait qu'un composant bien intentionné attribue la mauvaise grammaire au flux. Le navigateur et le serveur pouvaient respecter leur accord tout en laissant un troisième acteur produire un effet de cache qu'aucun des deux n'avait autorisé.

Un déguisement constant laissait le choix au script

En janvier 2011, le projet -04 imposait déjà le masquage des trames allant du client au serveur. Mais la clé, dérivée des valeurs de l'ouverture, restait constante pendant toute la connexion.

Une transformation fixe modifie bien l'apparence des données. Elle ne retire pas à l'émetteur la maîtrise du résultat si celui-ci peut la connaître. Dès qu'une application prévoit la règle, elle peut choisir une entrée qui donnera une sortie voulue. Le brouillage ne protège alors que contre un observateur ignorant, pas contre l'auteur des octets.

La propriété recherchée était plus précise : pour chaque trame, l'application pouvait déterminer le message logique ou connaître la transformation future, mais elle ne devait pas posséder les deux informations avant que l'image sur le fil soit engagée.

Un proxy donna un faux nom à la réponse

L'étude de 2011 Talking to Yourself for Fun and Profit observa les interfaces de sockets du navigateur lorsqu'un proxy transparent — en réalité un proxy d'interception — se trouvait sur le chemin. Certains équipements transmettaient un consentement ou une montée de protocole sans en comprendre la transition, puis lisaient la suite comme du HTTP.

La chaîne de causalité traversait plusieurs pouvoirs. Un site malveillant faisait ouvrir au navigateur une connexion vers son propre serveur. L'intermédiaire attribuait à des octets ultérieurs l'identité d'une autre ressource. Le serveur de l'attaquant répondait. Enfin, le cache rangeait cette réponse sous le nom trompeur, de sorte que d'autres utilisateurs pouvaient recevoir un contenu qui ne provenait pas de l'origine désignée.

L'expérience publicitaire de mars 2011 trouva ces conditions de pollution dans une part faible mais non nulle des chemins Java et Flash mesurés. Pour les ouvertures WebSocket expérimentales fondées sur Upgrade, huit cas réussirent parmi 47 338 ouvertures ayant atteint le test. Ces chiffres décrivent cet échantillon et cette époque ; ils ne mesurent aucun parc actuel.

La leçon la plus durable de l'étude ne tient pas au pourcentage. Ajouter un préfixe non HTTP ne pouvait prouver qu'aucun parseur défectueux ne le sauterait. La défense devait plutôt enlever au code hostile la possibilité de construire volontairement l'image binaire qu'un tel parseur accepterait.

Chaque trame reçut sa propre clé

Le projet -05 de février 2011 plaça une clé de 32 bits dans chaque trame masquée. Le client devait utiliser une source d'entropie forte et choisir une valeur neuve, imprévisible à partir des précédentes. La RFC 4086 rappelle pourquoi : une suite peut sembler aléatoire tout en restant devinable, notamment lorsqu'elle vient d'une horloge, d'un compteur ou d'une graine trop petite.

La RFC 6455 conserva ce mécanisme. Le bit MASK annonce les quatre octets ; l'octet de charge utile i est combiné par XOR avec l'octet de clé i mod 4. La longueur utile ne change pas et n'inclut pas la clé.

La clé est publique parce que le serveur doit défaire l'opération. Un observateur peut faire de même. L'ordre des décisions est le véritable contrôle : l'application engage le contenu avant de connaître la transformation imprévisible que le client appliquera. La confidentialité aurait répondu à une autre question.

Commencer l'envoi rendit la trame immuable

Une clé neuve ne suffit pas si l'application peut apprendre celle-ci grâce au début d'une longue trame puis réécrire la partie encore en mémoire. La RFC décrit ce cas : un préfixe connu révèle la transformation XOR répétée ; une queue modifiable pourrait alors être choisie pour prendre, une fois masquée, la forme d'une requête HTTP.

Dès que l'émission commence, la charge utile fournie par l'application ne doit donc plus être modifiable par elle. Toute donnée ajoutée ou changée passe dans une nouvelle trame, avec une nouvelle clé.

Ce point fait de la construction de trame un engagement temporel. Avant l'envoi, le script choisit le sens du message. Après le premier octet, l'implémentation cliente garde la garde d'une séquence fixée. Sans cette remise de contrôle, l'observation du début permettrait d'adapter la fin.

Le sens du trafic révélait la menace

Toutes les trames du client vers le serveur sont masquées ; aucune trame du serveur vers le client ne l'est. Une mauvaise direction oblige l'extrémité à fermer la connexion, éventuellement avec le code d'erreur de protocole 1002.

L'asymétrie suit la chaîne d'attaque. Pour donner une fausse identité à une réponse de cache, il faut d'abord fabriquer des octets ressemblant à une requête dans le sens navigateur-serveur. Un serveur hostile peut déjà produire des octets ressemblant à une réponse, mais cette moitié seule ne crée pas la requête trompeuse.

Cela ne rend pas la parole du serveur fiable. Authentification, autorisation, politique d'origine, validation du contenu et protection du canal restent des décisions distinctes. Le masquage inverse n'était simplement pas l'outil adapté à cette défaillance d'infrastructure.

TLS ne remplaça pas le masque

La règle s'applique aussi sous TLS. Un flux wss correctement protégé devient illisible pour les appareils qui ne terminent pas TLS, mais les deux preuves ne sont pas identiques. TLS protège le canal entre ses extrémités ; le masque limite ce que du code de page non fiable peut faire émettre par un client WebSocket conforme.

La séparation demeure utile lorsque TLS se termine sur une passerelle, lorsque le trajet interne diffère ou lorsqu'un client non navigateur utilise le protocole. La validité d'une trame ne dépend pas d'une hypothèse implicite sur la couche inférieure.

HTTP/2 et HTTP/3 gardèrent le contrat de trame

La RFC 8441 remplaça l'ouverture HTTP/1.1 par un CONNECT étendu sur un flux HTTP/2. Elle supprima, dans ce chemin, le traitement de Sec-WebSocket-Key et Sec-WebSocket-Accept, rendu inutile par le pseudo-en-tête :protocol. Elle maintint toutefois les considérations de sécurité de la RFC 6455, sauf la discussion propre au SHA-1 de l'ancienne ouverture.

La RFC 9220 porta le même dispositif vers HTTP/3 sans introduire une nouvelle exception. L'ouverture changea de transport ; la preuve directionnelle dans les trames WebSocket resta.

Cette continuité montre que le masque ne corrigeait pas seulement une orthographe particulière de Upgrade. Il organisait la relation entre choix applicatif, engagement du client et erreur d'intermédiaire — relation qui survit à la multiplication des flux.

Ce que prouve la clé publique

Une clé neuve ne prouve aucune identité. Elle ne dit ni qui parle, ni si l'origine est admise, ni si le serveur autorise l'action. Voir la clé dans une capture est normal ; revoir une clé ou prévoir la suivante indique au contraire que l'hypothèse de sécurité peut être rompue.

Le mécanisme ne répare pas non plus tous les proxys. Des clients ou serveurs non conformes peuvent encore exposer un intermédiaire vulnérable. La norme réduit ce qu'un trajet WebSocket conforme peut être amené à produire ; elle ne prend pas possession des caches du réseau.

La leçon historique tient dans cette modestie. Lorsqu'une infrastructure ancienne peut donner la mauvaise grammaire à des octets, annoncer correctement le nouveau protocole ne suffit pas toujours. Il faut parfois limiter les motifs dangereux qu'un participant non fiable peut déposer volontairement sur le chemin commun.

Sources et limites de la preuve

Les projets et RFC établissent l'évolution et les obligations. L'étude décrit une expérience bornée de 2011, pas la vulnérabilité actuelle d'un produit. Aucune source ne prouve le comportement d'un navigateur, proxy, CDN ou serveur contemporain nommé. Le masque n'est ni chiffrement, ni intégrité, ni authentification, ni preuve de provenance d'une réponse mise en cache.