Le protocole WebSocket est un protocole de la couche applicative du modèle OSI, qui permet de communiquer en full duplex (soit un canal de communication où l’information peut circuler simultanément dans les deux sens) entre un client (un navigateur) et un serveur web. En deux mots, il permet de créer des applications web de type « temps réel » — messagerie instantanée par exemple.
Il vient à bout de la latence créée par les communications half duplex où la communication ne s’effectue que dans un sens à la fois, caractéristique des solutions utilisées jusqu’alors pour réaliser ce genre d’applications. En effet, avec le protocole HTTP, le client initie une requête et attend une réponse, cela s’appelle une transaction. Chaque requête/réponse initie une transaction différente, alors qu’une WebSocket initie une transaction avec un long cycle de vie (plusieurs requêtes/réponses). De plus, le serveur peut envoyer des données sans qu’une requête préalable ait été effectuée.
La communication WebSocket
Le diagramme ci-dessous illustre le fonctionnement des WebSockets (de la connexion à la communication) :
Tout d’abord, une communication WebSocket est initiée au travers d’une communication HTTP via le WebSocket Handshake.
Le client indique au serveur qu’il veut initier une connexion WebSocket :
GET /chat HTTP/1.1
Host: www.websocket.com:8000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: http://www.websocket.com
Sec-WebSocket-Key: F3K8tSSU8iTVlhenxKqtbw==
DNT: 1
Connection: keep-alive, Upgrade
Cookie: X-Authorization=8jvbphlmk3DG8iXL0F4vraWBA
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Si le serveur accepte la connexion, il répond de la façon suivante :
HTTP/1.1 101 Switching Protocols
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: ILZpbBQBard/ixWRPPI6UOlh8hY=
On peut noter que la réponse HTTP indique le code 101, ce qui peut aider à trouver une connexion WebSocket dans une communication client/serveur. Les headers suivants sont rajoutés :
- « Connection: upgrade » et « Upgrade: websocket » dans la requête afin d’indiquer que l’on change de protocole
- « Connection: upgrade » et « Upgrade: websocket » dans la réponse afin d’indiquer que le changement est approuvé
- Le « Sec-WebSocket-Version » afin d’indiquer la version du protocole à utiliser
- « Sec-WebSocket-Key » : valeur utilisée par le serveur pour créer le « Sec-WebSocket-Accept » dans la réponse
- Le « Sec-WebSocket-Accept » : afin d’obtenir cette valeur, le serveur concatène la valeur obtenue via le client « Sec-WebSocket-Key » et la chaîne de caractère « 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 » (valeur fixée par défaut par la RFC 6455). Ensuite, il effectue le hash SHA-1 du résultat, qui sera enfin encodé en base64 avant d’être renvoyé. Cette valeur sert au client pour savoir que le serveur est disposé à initier une communication WebSocket avec lui. Attention, cela n’est en aucun cas un mécanisme d’authentification ou une protection de sécurité.
Une fois cette communication initiée, le client et le serveur communiquent de façon asynchrone. Le format des données échangées peut être de toute forme (HTML, JSON, Text…). Cependant, dans les faits, les libraires WebSocket les plus utilisées échangent au format JSON.
Attaques possibles et risques de sécurité
Si les WebSockets présentent un réel intérêt pour créer des canaux de communication full-duplex (en effet, sur les applications modernes, le serveur peut avoir besoin d’envoyer des données au client sans qu’il effectue de requête particulière, par exemple dans le cas d’une messagerie instantanée), cette technologie ne présente cependant pas d’avancée particulière en matière de sécurité. Ainsi, on retrouve les vulnérabilités habituelles du protocole HTTP avec parfois quelques spécificités. Ci-dessous, une liste non exhaustive des vulnérabilités et attaques possibles contre les WebSockets :
Contrôle de l’authentification
Le protocole WebSocket n’a pas de mécanisme natif permettant l’authentification, donc lors du développement, une solution propre doit être implémentée, que ce soit par des cookies, un JWT ou par authentification HTTP (Basic/Digest). Lors d’un test d’intrusion, il faut alors systématiquement vérifier s’il y a effectivement un système d’authentification et s’il est implémenté correctement (accès à des fonctionnalités sans authentification).
Autorisation et contrôle des autorisations
Comme pour l’authentification, il n’y a pas de système pour gérer les autorisations (que les utilisateurs n’aient accès uniquement aux données et services auxquels ils devraient avoir accès) dans le protocole WebSocket. Cela donne ainsi la possibilité à un attaquant d’élever ses privilèges verticalement ou d’accéder aux données d’un utilisateur de même niveau de droit que lui.
Lors d’un pentest, le contrôle des droits sera testé en détail pour essayer d’obtenir une élévation de privilèges.
Risques liés aux entrées des utilisateurs
Les données rentrées par les utilisateurs représentent, aussi via les WebSockets, la source majeure d’attaques — XSS, injections SQL, injections de code, etc. Toutes les entrées doivent être assainies avec la méthode la plus appropriée au contexte avant d’être utilisées.
Le risque de sniffing (ou écoute de réseau)
La transmission de données par le protocole WebSocket se fait en clair comme pour le HTTP. Il est donc possible de récupérer ces données par des attaques de l’homme du milieu (Man-in-the-Middle). Pour éviter la fuite d’information, il faut mettre en place le protocole WebSocket Secure (wss).
Rappelons que tout comme pour HTTPS, wss ne veut pas dire que l’application web est sûre, il ne s’agit que du transport des données chiffrées via TLS.
Le Cross-Site Websocket Hijacking (CSWH)
Le Cross-Site WebSocket Hijacking est une attaque semblable à la CSRF (Cross-Site Request Forgery). En effet, ces deux attaques sont possibles lorsque le serveur ne se base que sur les données d’authentification de la session (cookie) pour effectuer une action authentifiée.
Pour le CSRF , l’attaquant cache une requête dans un lien ou un autre élément qui serait visible sur l’écran de sa victime (authentifiée). Il l’incite à cliquer dessus pour provoquer l’action demandée par la requête cachée. L’attaquant peut ainsi faire faire des actions néfastes à la victime à son insu. L’inconvénient était qu’il ne pouvait pas voir la réponse du serveur à la requête (en raison de l’origin policy), inconvénient qui n’existe plus avec les WebSockets, car les WebSockets n’implémentent pas d’origin policy. Cela permet à l’attaquant de se créer une connexion full duplex avec les identifiants de la victime.
Dans les faits, l’attaque Cross-Site WebSocket Hijacking est possible quand le WebSocket Handshake est vulnérable au CSRF. En effet, le canal de communication entre les deux parties (client/serveur) se crée en fonction de l’origine de la demande d’ouverture. Ainsi, si la demande de changement de protocole ne se base que sur les cookies, un attaquant peut alors piéger une victime afin qu’elle initie une demande avec sa session mais sur le serveur de l’attaquant.
Une fois piégée, l’attaquant peut communiquer avec le serveur via la WebSocket à l’insu de la victime. Il peut donc comme avec une CSRF classique effectuer des actions à la place d’un utilisateur, mais aussi lire les messages du serveur envoyés via les WebSockets.
Afin de résoudre cette vulnérabilité, il convient de rajouter un token unique par session et non devinable en paramètre de la requête du handshake.
Comment tester la sécurité des WebSockets ?
Actuellement, les deux principaux outils pour tester les WebSockets sont Burp et ZAP de l’OWASP. Ces outils permettent d’intercepter et de modifier des trames WebSockets à la volée. Le nombre d’outils disponibles pour pentester les implémentations de WebSockets est encore assez faible.
Notons également que parmi les outils de développement de Chrome, on en trouve un qui permet de voir le trafic des Websockets. Une fois les outils à disposition, l’audit des WebSockets se passe comme pour des requêtes HTTP normales (tests d’injection, tests de droits, tests de workflow…).
Si le protocole WebSocket est vulnérable à de nombreuses attaques, une bonne configuration permet déjà de limiter drastiquement les risques, comme c’est le cas pour le protocole HTTP.