Pourquoi SSH envoie 100 paquets à chaque frappe
(eieio.games)- Un cas d’analyse a mis au jour le phénomène suivant dans une session SSH : des centaines de paquets sont envoyés pour une seule frappe, puis la cause a été identifiée
- L’analyse avec
tcpdumpa montré que la plupart des paquets étaient des messages répétitifs de 36 octets, émis à un intervalle d’environ 20 ms - La cause était la fonctionnalité de « keystroke timing obfuscation » ajoutée à SSH en 2023, qui envoie de nombreux paquets de « chaff » (
SSH2_MSG_PING) pour masquer le timing de saisie de l’utilisateur - En désactivant cette fonctionnalité ou en modifiant le serveur pour qu’il n’annonce plus l’extension
[email protected], l’utilisation CPU et la bande passante chutent de plus de moitié - Cet exemple montre qu’une fonctionnalité de sécurité de SSH peut devenir une charge importante dans les applications où les performances en temps réel sont critiques (par exemple les jeux)
Découverte du problème
- Lors de tests sur la TUI d’un jeu haute performance exécuté via SSH, il a été constaté qu’une seule frappe générait 270 paquets
- D’après
tcpdump, 66 % étaient des messages de 36 octets, 33 % des ACK TCP, et le reste une petite quantité d’autres données - En moyenne, 90 paquets/s étaient transmis, avec un intervalle d’environ 11 ms
- D’après
- Pendant les tests, le serveur avait été mal configuré pour n’envoyer que le message « your screen is too small », et dans ce cas l’utilisation CPU et de la bande passante a été divisée par deux
- Comme aucune donnée de jeu ne devait être transmise, l’utilisation CPU aurait dû être proche de 0 %, mais elle restait autour de 50 %
- Cela a conduit à soupçonner un surcoût de communication propre à SSH
Enquête
- Comparaison du trafic SSH en fonctionnement normal et en état d’erreur à l’aide de
tcpdump- Même en état d’erreur, des paquets de 36 octets continuaient à être émis toutes les 20 ms
- Le même motif a été observé avec le client SSH par défaut de macOS
- L’analyse du fichier pcap avec Claude Code a montré que
- sur un total de 413 703 paquets, 66 % faisaient 36 octets et 34 % étaient des ACK de 0 octet
- c’était le client SSH qui générait activement les paquets
Cause profonde
- Le journal de débogage SSH (
ssh -vvv) a affiché le message suivantobfuscate_keystroke_timing: starting: interval ~20ms obfuscate_keystroke_timing: stopping: chaff time expired (101 chaff packets sent)- L’intervalle de 20 ms et les dizaines à la centaine de paquets de chaff correspondaient au motif réellement observé
- La cause était la fonctionnalité d’obfuscation du timing des frappes ajoutée à SSH en 2023
- Elle envoie des paquets de « chaff » aléatoires afin d’éviter que le schéma de vitesse de frappe de l’utilisateur ne soit exposé
- Utile pour la sécurité, elle provoque toutefois une surcharge excessive dans les environnements où la latence est critique
Solution
- Côté client, la fonctionnalité peut être désactivée avec l’option
ObscureKeystrokeTiming=no- Après application, l’utilisation CPU et la bande passante ont fortement diminué, tout en maintenant une transmission des données normale
- Côté serveur, une réponse a consisté à retirer l’annonce de l’extension
[email protected]dans la bibliothèque SSH de Go- Après avoir annulé le commit concerné et relancé les tests, les résultats étaient les suivants
- utilisation CPU 29.9 % → 11.6 %,
appels système 3.10s → 0.66s,
opérations de chiffrement 1.6s → 0.11s,
bande passante 6.5Mbit/s → 3Mbit/s
- utilisation CPU 29.9 % → 11.6 %,
- Les performances se sont améliorées de plus de 50 %
- Après avoir annulé le commit concerné et relancé les tests, les résultats étaient les suivants
Expérience de débogage avec un LLM
- Claude Code a permis d’automatiser l’analyse de
tcpdumpettshark, ce qui a aidé à cerner rapidement la cause du problème- Il a aussi été possible de conserver un modèle mental du problème en observant l’exécution des commandes en temps réel
- ChatGPT a au contraire mal interprété le comportement de SSH en le jugeant « normal », ce qui a aussi mis en évidence des différences entre modèles
- Les LLM ne remplacent pas l’ensemble du processus de résolution, mais se montrent très efficaces comme outils d’analyse d’appoint
- Il s’agit d’un exemple où le raisonnement humain et l’analyse d’un LLM ont été combinés pour résoudre un problème complexe de performances réseau
1 commentaires
Réactions sur Hacker News
Forker la bibliothèque crypto de Go me semble un peu effrayant
Je réfléchis à une manière de maintenir mon petit patch en sécurité
En réalité, je pense que ce genre de fonctionnalité devrait être intégré en upstream comme option de la bibliothèque SSH
Dans des environnements non fiables, envoyer des paquets de chaff (bruit) est un bon choix par défaut, mais on veut aussi souvent économiser de la bande passante
La bonne solution serait d’ajouter une option permettant au serveur de signaler au client que ce n’est “pas nécessaire”, puis au client d’accepter ou d’émettre un avertissement
Il ne s’applique qu’aux sessions TTY, et le client peut le désactiver
Mais ici, c’est un cas exceptionnel où le serveur sait à l’avance que la connexion n’est pas sensible
Dans la plupart des cas, le client s’attend à ce que le paramètre ObscureKeystrokeTiming soit respecté
La bibliothèque crypto est une base de code très normative, au point qu’on ne peut même pas modifier l’ordre des cipher suites TLS
Cela ressemble à un cas d’usage SSH très spécifique
Si on l’expose trop largement, on risque un scénario de type « on configure et on oublie », ce qui pourrait au contraire affaiblir la sécurité
J’ai aussi connu les communications au modem 1200 bps, et les modems 56K étaient en pratique très exagérés
Vers 1994, je travaillais dans une école militaire au Royaume-Uni et j’ai découvert le WWW pour la première fois ; à l’époque, je m’étais dit « bof »
Avec le recul, le changement d’époque est vraiment saisissant
Je n’avais jamais entendu parler de cette fonction d’obfuscation, c’était intéressant
Pour déboguer le comportement de ssh, une bonne méthode consiste aussi à patcher le chiffrement
Nonepour voir directement le contenu des paquetsSi la sécurité n’est pas importante mais que la performance l’est, comme pour un jeu en terminal, on peut aussi envisager d’utiliser telnet
Je ne savais pas que SSH faisait ce genre de chose
Je comprends pourquoi c’est activé par défaut, mais dans mon environnement il vaudrait sans doute mieux le désactiver
Je pense donc régler
ObscureKeystrokeTiming=no. Y a-t-il une raison de ne pas le faire ?(1) Il n’est pas toujours facile de savoir quand une personne saisit un secret, et l’ensemble de son activité peut faire l’objet d’une analyse de motifs
(2) C’est une attaque faisable même dans un labo universitaire — voir cet article USENIX et ce cas de recherche
(3) Sur un Internet dominé par le trafic vidéo, sacrifier la sécurité pour économiser quelques octets de frappes n’a guère de sens
Si un attaquant analyse le timing des frappes, il peut potentiellement estimer les commandes et les motifs du mot de passe chiffré
Bien sûr, le déchiffrement reste difficile puisque la clé de session change à chaque fois, mais la possibilité existe
Moi aussi, je copie-colle depuis un gestionnaire de mots de passe pour la plupart de mes mots de passe
La plupart des gens ont l’impression qu’ils peuvent désactiver des fonctions de sécurité de SSH sans aucun problème, mais c’est surtout qu’ils ont eu de la chance
Si la performance est vraiment prioritaire, mieux vaut Telnet ; si la sécurité est vraiment prioritaire, la combinaison ContainerSSH + OAuth2 est préférable
En 2004, j’avais fait des recherches sur l’analyse des délais entre frappes dans les sessions SSH pour deviner les commandes
Voir cette analyse de l’époque
Le patch de 2023 a donc fini par résoudre ce problème
Support de présentation
Le temps passe vraiment vite
Je ne suis pas sûr que Claude ait réellement aidé pour le débogage
L’auteur semblait déjà savoir dans quelle direction aller, et Claude donnait surtout l’impression d’abonder dans son sens
Le fait que Claude dise des choses comme “Holy Cow!” est un peu agaçant
Quand je débogue le fonctionnement d’un système avec Claude, même sans réponse directe, cela m’aide à structurer ma compréhension et à garder la motivation
Wiki sur le Rubber Duck Debugging
Vu que l’auteur a gardé la réaction “holy cow” dans son billet, Claude semble avoir bien senti l’ambiance
En utilisant TCP_CORK, on peut réduire le nombre de paquets sans ajouter de latence
Désactiver TCP_NODELAY est aussi une option, mais au prix d’une augmentation de la latence
Si on met le socket en cork, le noyau met les données en tampon puis les envoie au moment de l’uncork ou à l’atteinte du MSS
Autrement dit, cela regroupe les paquets avant envoi
Référence
Je recevrai toujours les ping, mais je pourrai probablement réduire le nombre de pong envoyés
J’ai déjà essayé TCP_NODELAY, mais la latence augmentait trop pour mon jeu
Ancien post HN
Dans une logique d’obfuscation, la fusion temporelle (coalescing) semble impossible
L’expression « The smoking gun! » m’a fait rire
Je ne suis pas anglophone natif, mais Claude l’utilise souvent, c’est comme ça que je l’ai apprise
Maintenant, elle se répand presque comme un tic de langage
Je trouve dommage cette dépendance aux LLM
Avec Wireshark, ça aurait sans doute été résolu plus vite en regardant simplement une capture de paquets
Le dissector SSH est assez mature
Même avec tcpdump sur une seule frappe, on obtient des centaines de paquets chiffrés
Au final, grâce au LLM, l’auteur a appris quelque chose d’intéressant et l’a partagé, donc cela avait de l’intérêt
Après le message NEWKEYS, il ne parse plus, et même avec un patch en chiffrement
none, il n’interprète pas complètement le fluxIl y a encore matière à amélioration
Apprendre en s’aidant d’outils a tout à fait sa valeur
Il est difficile d’en tirer des informations réellement utiles à partir d’une simple capture de paquets
Je ne vois pas en quoi ce serait malheureux
En 2023, SSH a ajouté une fonctionnalité d’obfuscation du timing des frappes
Comme on peut deviner les caractères à partir de la vitesse de frappe, SSH mélange des paquets de chaff pour empêcher l’attaquant de les distinguer
Mais cela me semble être une mauvaise approche
Si on le voulait vraiment, il suffirait d’envoyer toutes les frappes à intervalles de 50 ms
L’implémentation actuelle regroupe par tranches de 20 ms, puis cesse d’envoyer du chaff après un certain temps sans saisie
La vocation première de SSH, c’est la sécurité, mais si on n’en a pas besoin, on peut se demander pourquoi utiliser SSH
Par exemple, netcat (nc) est installé par défaut sur la plupart des plateformes
Avec SSH, il y a aussi d’autres considérations comme la performance ou la commodité
L’auteur disait simplement que la fonctionnalité de confidentialité liée à l’obfuscation des frappes ne lui était pas utile
Il peut tout à fait vouloir conserver le chiffrement et les garanties d’intégrité
Autrement dit, il choisit de désactiver seulement une partie des fonctions de sécurité de SSH tout en gardant l’essentiel