Websockets

Diese Seite beschreibt, was Websockets sind, wie und wo sie verwendet werden und was Sie als Administrator darüber wissen müssen.

HTTP, Pushmail und Websockets

Es geht, wie immer, um die Kommunikation zwischen Clients und Servern mit unterschiedlichen Anforderungen. Drei klassische Verfahren kennen die meisten Administratoren schon heute.

  • Request/Response + Polling
    Webbrowser bauen per TCP eine Verbindung auf, um dann per HTTP/HTTPS eine Anfragen zu stellen und die Antwort zu erhalten. Damit ist die Transaktion abgeschlossen und der Webserver kann keine weiteren Daten nachliefern. Es liegt am Client, eine neue Anfrage nach Daten zu stellen. Das Polling kostet Bandbreite, Energie, belastet den Webserver wenn sich nichts geändert hat und kann dennoch nie Realtime sein.
  • Ausstehende HTTP-Requests
    Schon lange nutzt daher z.B. ActiveSync ein HTTP-Polling mit ausstehenden Requests, d.h. der Client stellt eine HTTP-Anfrage aber der Webserver antwortet nicht sofort, sondern erst wenn es etwas zu vermelden gibt. Die TCP/Proxy-Verbindung bleibt hoffentlich lange genug bestehen, damit die Antwort auch nach Minuten wieder zurückkommt. Auch andere Dienste nutzen das Verfahren aber es ist immer noch ein Request/Response-Protokoll. Der Client kann nach Absenden seiner Anfrage keine weitere Anfrage über den gleichen Kanal absenden.
  • Streaming
    Wenn Sie nun aber YouTube, Netflix u.a. Dienste nutzen, dann sehen Sie einen fast kontinuierlichen Datenstrom vom Server zum Client. Aber auch hier stellt der Client eine Anfrage und ist danach nur Empfänger. Eine echte bidirektionale Kommunikation findet nicht statt.

Hier kommen nun Websockets in den Fokus, die eine bidirektionale transparenter Verbindung zwischen Client und Server ermöglichen. Das startet alles wie gehabt wieder mit einem klassischen HTTP/HTTPS-Request, für den der Client natürlich eine DNS-Anfrage stellt, eine TCP-Verbindung zum Ziel oder über einen Proxy herstellt aber dann mit dem Request ein "Upgrade" zu Websockets anfordert.

GET /MSXFAQSockets HTTP/1.1
Host: sockets.msxfaq.de
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: <base64-codierte Zufallszahl>
Sec-WebSocket-Origin: http://msxfaq.de.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

Der Server antwortet darauf mit den unterstützten Diensten.

HTTP/1.1 101 Switching Protocols
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Accept: <base64 codierter Hash der gesendeten Zufallszahl>
Sec-WebSocket-Protocol: chat

Achtung:
Wenn sie im Browser per HTTPS von einem Server die HTML-Seiter und JavaScript laden, dann erlauben moderne Browser nur die Nutzung von WSS, also Secure WebSocket und keine Websockets ohne TLS.

Wenn zwischen Client und Server ein HTTP-Proxy geschaltet ist, muss dieser damit natürlich umgehen können. Ansonsten sollte er dem Client einen Fehler liefern, so dass der Client entweder auf die alte Request/Response-Technik zurückfällt oder anderen Protokolle nutzt. Der "Sec-WebSocket-Key" hat die Aufgabe, dass ein Proxy die Daten nicht cached und die Antwort wirklich vom anderen Server geliefert wurde.

Wenn die Antwort dem Client passt, dann schalten sowohl Clients als auch Server auf einen transparenten Mode um und können über die bestehende Verbindung quasi beliebige Daten in jede Richtung senden. Durch den Wegfall von GET/POST und HTTP-Antworten mit all dem Overhead der Header und Encoding des Body etc. können über Websocket-Verbindungen sehr schnell und effektive Daten übertragen werden.

Client und Server

Damit ist aber auch klar, dass der Client nun nicht mehr der klassische "Browser" ist, der HTML-Seiten anfordert und anzeigt, sondern meist nur noch eine Webseite mit JavaScript vom Webserver geladen wird, und mittels JavaScript dann de Websocket-Verbindung zum Datenservice aufgebaut wird. JavaScript kann dann die gewünschten Daten abfragen und direkt die Felder der lokalen HTML-Seite verändern.. Voraussetzung ist dazu natürlich eine halbwegs aktuelle Version des Browsers, z.B.:

Internet Explorer: Version 10 oder höher
Firefox: Version 6 oder höher
Chrome: Version 14 oder höher
Opera: Version 12.10 oder höher
Safari: Version 6 oder höher

Auch beim Webserver muss es eine Änderung geben, denn eine klassische ASPX/PHP-Seite ist ja darauf ausgelegt, dass von ihr ein Request verarbeitet und dann das Ergebnis ausgeliefert wird. So eine Umsetzung ist kein Dauerläufer und daher sind nach meinem Wissen die klassischen Webserver keine Websockets-Server aber können die Verbindungen an solche Dienste weitergeben.

Als Entwickler schreiben Sie daher ihren Service in einer ihnen passenden Programmiersprache, um nun über Websockets-Verbindungen eine "Stream"-Kommunikation zu ermöglichen. DAs ist quasi mit einem Server gleichzusetzen, der früher TCP-Sockets genutzt hat, nur dass die Verbindung nun über HTTP/HTTPS startet und erst dann transparent wird. Auch hier gibt es je nach Umgebung schon einige Bibliotheken.

.NET: SuperWebSocket, Net.WebSockets.WebSocket
C++: Libwebsockets
Node.js: Socket.IO,  WebSocket-Node, ws
Java: Jetty
Ruby: EventMachine
Python: pyWebSocket oder Tornado

Das bedeutet natürlich, dass die Websocket-Verbindung nicht mehr durch den Webserver selbst bedient sondern maximal durchgereicht wird oder die Websocket-Verbindung zu einem anderen Port oder Server aufgebaut wird oder der Websocket-Service selbst auch einen kleinen Webserver bereitstellt.

Nutzer von Websockets

Auf der Suche nach Produkten, die heute schon Websockets nutzen, brauchen Sie gar nicht weit zu gehen. Die effektive bidirektionale Verbindung bietet sich nicht nur für Chat-Clients an, sondern kann auch Interaktion in Webseiten und auf Smartphones bereichern. Als Administrator müssen Sie dies aber auch wissen, denn wenn Sie einen HTTP-Proxy einsetzen, sollten Sie hier ggfls. die Konfiguration optimieren und auch eine Firewall/NAT-System sollte dahingehend geprüft werden, wie solche teilweise sehr lange beständige Verbindungen ihr System zusätzlich belasten.

  • Copilot
    Wei Copilot aktuell in aller Munde ist, ist ein Blick auf die Netzwerkanforderungen interessant

    Microsoft Copilot for Microsoft 365 requirements
    https://learn.microsoft.com/en-us/microsoft-365-copilot/microsoft-365-copilot-requirements
  • Office Apps, Loop etc.
    Die gleiche Quelle legt quasi schon offen, dass auch die Microsoft 365 Apps, Teams u.a. mit Websockets arbeiten. Bei Teams ist das für den Chat und die Meldung eingehender Anrufe ja verständlich. Für die Übertragung von Audio/Video könnte Teams auch Websockets nutzen aber ratsam wäre das nicht, da TCP als darunterliegendes Protokoll keine gute Plattform für RTP-Daten ist. Anders sieht es aber bei der Zusammenarbeit mehrerer Personen am gleichen Office-Dokument aus, wo sie direkt sehen, wer welchen Abschnitt gerade bearbeitet und Änderungen möglichst zügig angezeigt werden sollen. Da wäre es kontraproduktiv, wenn ihr Browser jedes Mal ein "Reload" des Dokuments machen würde.
  • Teams Client API
    Auf der Seite Teams 3rd Party Client-API habe ich beschrieben, dass eine lokale API z.B. zur Anbindung von Teams Controllern wie Elgado Steamdeck ebenfalls eine Websocket-Schnittstelle anbieten. Diese Funktion ist also nicht auf reine Server beschränkt. Der Server kann auch ihr Browser sein.
  • IoT und MQTT
    Natürlich sind die kleinsten Computer aus der Bastlerszene dankbare Clients und Server von Websockets. Aufgrund von Systembeschränkungen (CPU, RAM) und der oft kleinen Daten sind komplette Webseiten oder Webserver mit aktiv errechneten Inhalten kaum möglich. Eine lokal ausgelieferte HTML-Seite mit etwas JavaScript zum Anrufen der weiteren Daten ist dort die geniale Lösung. Auch MQTT-Server bieten Websockets neben dem TCP-RAW-Port an.

Es gibt noch viele weitere Einsatzfelder und in allen Situationen, bei denen es um besonders schnelle Reaktionen geht, sind Websockets im Gegensatz zu HTTP-Polling im Vorteil.

Websocket mit NGINX, IIS, Apache und ReverseProxy

Wer über HTTP spricht, denkt immer an einen Webserver, der statische und dynamische per PHP, ASPX etc. generierte Dokumente ausliefert oder Webservices bereitstellt. Websockets funktionieren aber als beständige Verbindung und daher ist dies nach meinem Wissen immer ein separater Prozess.

Es gibt aber nun drei Optionen, wie die Lösung aussehen kann:

  1. Webserver als Reverse Proxy
    Der Client verbindet sich mit dem Webserver, um z.B. die HTML-Seite und JavaScript zu erhalten, welches dann eine Websocket-Verbindung zum gleichen Server aufbaut, der die Verbindung als ReverseProxy aber an einen anderen Prozess weiterreicht. Für den Client ist dies nicht sichtbar.
  2. WebServer und WebSockets parallel
    Der Client könnte auch zum Webserver gehen, um wieder die HTML um JavaScript-Dateien zu erhalten. Das JavaScript baut dann aber direkt eine Verbindung zum Websocket-Service auf, der dann mit einem anderen Namen und eigenem Zertifikat erreichbar sein muss
  3. WebSocket-Service ist auch WebServer
    Wenn hinter der Adresse kein ausgewachsene Webseite betrieben wird, dann kann natürlich auch der Websocket-Service eine rudimentäre Funktion eines Webeservers enthalten, um die erforderliche HTML und JavaScript-Datei auszuliefern.

Alle drei Varianten habe ich schon gesehen. Gerade in der IoT-Welt mit Prozessoren wie einem ESP8266, Arduino Plattform etc. wird oft die Variante 3 genutzt, wenn man nicht auf Websockets zugunsten eines Webservice verzichtet.

Websocket mit Proxy

Ihr Client ist intern und nutzt einen Service im Internet. Im Homeoffice wird sich ein DSL-Router normalerweise nicht daran stören und die Pakete passieren lassen aber in Firmen sieht es anders aus. Hier wird sehr gerne ein eigener lokale Proxy, z.B. Squid o.ä., oder ein Cloud Proxy (Xscaler, Cisco Umbrella) eingeschaltet und der Client angewiesen, diesen Dienst zu nutzen. Damit das sicher ist, wird eine direkte Verbindung der Clients zum Internet unterbunden. Der Client muss also eine Verbindung zum Proxy aufbauen und dann mittels "CONNECT"-Request die Abfrage nach extern einleiten. Der Proxy sieht anhand des CONNECT natürlich zumindest den Hostnamen des Ziels. Wenn er nicht zugleich SSL-Inspektion versteht, sieht er natürlich nicht die URLs von HTTPS bzw. WSS-Verbindungen. Bei unverschlüsselten Paketen kann ein Proxy aber schon die Requests sehen und auch verändern oder verhindern.

Controls client-initiated and server-confirmed switching from HTTP to another protocol (or to several protocols) using HTTP Upgrade mechanism defined in RFC 7230 Section 6.7. Squid itself does not understand the protocols being upgraded to and participates in the upgraded communication only as a dumb TCP proxy. Admins should not allow upgrading to protocols that require a more meaningful proxy participation.
Default Value: Upgrade header dropped, effectively blocking an upgrade attempt.
Quelle Squid configuration directive http_upgrade_request_protocols https://www.squid-cache.org/Doc/config/http_upgrade_request_protocols/

Sie sollten also prüfen, ob ihre ausgehende Verbindung zumindest bei unverschlüsselten Websocket-Diensten die Funktion nicht blockiert. Ein guter Entwickler schreibt in seinen Code eine Fehlerbehandlung, die auf das Problem hinweist. Allerdings gibt es wohl auch Dienste, die Websockets versuchen und wenn dies nicht funktioniert, auf HTTP-Polling oder HTTP-OutstandingRequests und einen Webservice zurückschalten. Irgendwie funktioniert es dann natürlich aber nicht so schnell und bandbreitenschonend, wie erwartet.

Bei meinen Tests mit Squid (Siehe Squid-Proxy auf WSL mit Kerberos) waren Websockets über HTTP in der Standardkonfiguration geblockt aber eine "wss://"-Verbindung möglich.

Das ist auch verständlich, denn wenn Sie die Arbeitsweise eines Proxy-Servers kennen, dann wissen Sie, dass ein Proxy ohne SSL-Inspection nur bei einer unverschlüsselten "ws://"-Verbindung auch den "Upgrade"-Request des Clients und die "101 SWITCHING"-Antwort sehen kann. Wenn die Websocket-Verbindung aber per HTTPS erfolgt dann verbindert sich der Client zuerst mit einen "CONNECT" über den Proxy, und die weitere Kommunikation ist für den Proxy gar nicht mehr sichtbar. Er könnte nur anhand der vermutlich bidirektionalen Kommunikation mit vielleicht symmetrischen Daten vermuten, dass dies hier kein HTTP-Surfen ist.

Ich gehe davon aus, dass die meisten Server bei Kunden hier auch die Standardwerte nutzen. Ansonsten gibt es ja entsprechende Testseiten.

Websockets und Caching

Die meisten Websocket-Verbindungen werden vermutlich eher wenig Daten übertragen die zudem pro Client individuell sind. Aber Microsoft Teams nutzt Websockets angeblich auch bei ihrem eCDN, um Audio/Video zu übertragen.

Wenn viele Clients hinter einem Proxy am Firmenstandort die Videos als "Schnipsel" bekommen, dann könnte ein Proxy-Server diese doch auch cachen? Diese Überlegung war der Grund, dass es nun die Seiten Websockets und Squid-Proxy auf WSL mit Kerberos gibt. Ich habe mir einfach einen Squid-Server installiert und etwas mehr über die Funktion von Websockets dazugelernt.

Allein anhand der Funktionsweise von Websockets wird aber sehr schnell klar, dass hier ein Caching kaum möglich sein dürfte, da es pro Client eine individuelle TCP-Verbindung zu einem Backend ist. Auch wenn das Backen das gleiche System ist, kann ein Proxy kaum die an sich gleichen Datenströme zusammenführen. Der Proxy müsste erkennen, dass mehrere Client die gleichen TCP-Anfragen per Websockets an den Server senden und diese dann bei sich terminieren. Leider kenne ich keinen eCDN-Proxy, der dies kann und Microsoft scheint diese Funktion auch nur als eCDN-Client als P2P-Funktion bereitzustellen.

Es gibt aber noch einen zweiten Aspekt, der ein Caching von Websocket-Verbindungen deutlich erschwert: Der Proxy kann ohne TLS-Inspection gar nicht erkennen, dass es sich um eine Websocket-Verbindung handelt. Der Client startet über den Browser einen "CONNECT" und danach sind alle Daten verschlüsselt.

Eine Optimierung des Audio/Video-Datenstroms von LiveEvents/Townhall-Meetings ist über einen Proxy daher nicht ohne Mithilfe von Microsoft möglich.

Test mit Invoke-Webrequest

Ob ihr Netzwerk, ihr Proxy oder ihre Firewall auch für Websockets transparent sind, würde ich gerne testen. Eine mögliche Testseite ist der MQTT-Server Mosquitto, welcher nicht nur über TCP auf Port 1883 sondern auch über Websockets erreichbar ist.


Quelle https://test.mosquitto.org/

Nun fehlt uns nur noch ein passendes Testwerkzeug. Websocket können Sie im Browser mit JavaScript nutzen und es gibt jede Menge Beispiele dafür. Ich habe mich für eine PowerShell-Version entschieden, welcher ich eine Websocket-URL gebe und ich zumindest ermitteln kann, ob ich die Gegenstelle erreichen kann, auch wenn ich vielleicht ihre Nutzdaten erst einmal nicht verstehe. Einen vollwertigen Websocket-Client zu schreiben ist möglich aber erfordert einen asynchronen Ansatz, d.h. die Klasse ruft eine hinterlegte Funktion auf. Daher habe ich erst einmal den Start mit einem WebRequest simuliert.

param (
    [string]$uri =  "http://test.mosquitto.org:8080/ws"
)

$result = Invoke-Webrequest `
            -uri $uri `
            -headers  @{'Connection' = 'Upgrade';
                        'Upgrade' = 'websocket';
                        'Sec-WebSocket-Key' = ([Convert]::ToBase64String((1..16|%{[byte](Get-Random -Max 256)})));
                        'Sec-WebSocket-Version' = '13';
                        'Sec-WebSocket-Extensions' = 'permessage-deflate; client_max_window_bits';
                        'Sec-WebSocket-Protocol' = 'mqtt';}

Ich musste aber für "MQTT over Websockets" noch einige Werte addieren, damit die Gegenseite auch wirklich einen 101 geliefert hat.

Nur kommt Invoke-Webrequest dann natürlich nicht zurück, da die Verbindung ja "transparent" ist. Ich kann natürlich mit dem Parameter "-OperationTimeoutSeconds" einen Abbruch erzwingen, der dann aber einen Fehler bewirkt. Invoke-Webrequest kann daher grob zum Testen genutzt werden aber nicht als Anwendung, es sei denn die Gegenseite, sendet direkt die Daten und schließt danach die Verbindung wieder. Aber dann haben Websocket keinen Vorteil gegenüber einem einfachen GET/POST oder einer HTTP Chunked-Übertragung.

Wireshark

Die Analyse von Websocket-Verbindungen mittels eines Netzwerkmittschnitts ist bei unverschlüsselten Verbindungen (ws://) relativ einfach möglich aber da heute alle Verbindungen eigentlich immer verschlüsselt sind, sehen wir hier auch nur am Anfang den TCP und TLS-Handshake und danach nur noch "Pakete". Aber auch wenn wir die Nutzdaten nicht sehen können, so könnten wir anhand der Größe und Frequenz der Pakete vielleicht einen Hinweis auf Websockets.

Da ich in meinem vorigen Beispiel absichtlich eine unverschlüsselte Verbindung gewählt habe, konnte ich in Wireshark auch den Request und die 101 Antwort sehen:

Das ist schon mal ein ganz einfacher Test eines Proxy-Servers der Firewall auf Durchlässigkeit.

Test im Browser

Websockets werden natürlich fast überall im Browser genutzt, weil es mit JavaScript sehr einfach damit einen Kommunikationskanal zu einem Backend aufzubauen und moderne Apps zu schreiben. Sie brauchen nicht lange zu suchen, bis Sie entsprechende Adressen finden, die per HTTP/HTTPS eine HTML-Seite mit JavaScript-Code herunterladen und starten. Einige Seiten machen einfach Generische Tests, ob Websockets ganz generell funktionieren.

  • Websockettest.com
    https://websocketstest.com/
    Einfacher Test im Browser, inwieweit Websockets nach extern funktionieren und ein Proxy diese unterstützt:

Websocket mit Debugger

Wie sie am vorigen Beispiel mit https://libwebsockets.org/testserver/ schon gesehen haben, können Sie natürlich im Browser über ein eingebauten Debugger auch den Netzwerkverkehr und damit die Websocket-Pakete mitschneiden, selbst wenn Sie verschlüssel sind. Sie können die Websockets am eigenen Icon gut erkennen.

Tipp: Der Debugger muss schon laufen, ehe die Websocket-Verbindung gestartet wird. Daher beginne ich immer einem leeren Tab, starte den Debugger und gebe dann die URL in der Adressliste ein.

Die Analyse funktioniert mit Text-Informationen, z.B. JSON-Dateien ganz gut. Sollte eine Applikation allerdinge mit Binärdaten arbeiten, dann wird die Analyse kniffliger. Wenn die Daten nicht verschlüsselt sind, dann kann WireShark vielleicht das bessere Werkzeug sein. Seit der Version 1.8.0 (http://www.wireshark.org/docs/relnotes/wireshark-1.8.0.html) kann Wireshark auch Websockets filtern und decodieren.

Sie können aber im Chromium Debugger sogar direkt JavaScript eingeben und ausführen lassen. Mit wenigen Zeichen können Sie eine Websocket-Instanz einrichten und Aktionen ausführen. Hier z.B. eine einfache Ausgabe von empfangenen Nachrichten:

var webSocket = new WebSocket('ws://address:port');
webSocket.onmessage = function(data) { console.log(data); }

Wenn wir das Beispiel von https://libwebsockets.org/testserver/ nehmen, dann können Sie die 20 Zahlen/Sekunde auch so in die Konsole ausgeben. Starten Sie den Debugger wieder in einem Tab und geben einfach folgendes ein

var webSocket = new WebSocket('wss://libwebsockets.org/');
webSocket.onmessage = function(data) { console.log(data); }

Sobald nun eine Meldung vom Websocket ankommt, wird diese auf der Console ausgegeben. Umgekehrt können Sie natürlich auch eine Meldung über Websockets versenden.

webSocket.send('Tetsnachricht');

Ich habe dazu mal mit dem Websocket-Server missbraucht, der ein QR-Code Angreifer mit auf QR-Code Phishing mit Microsoft 365 geliefert hat. Ich konnte mich verbinden und er hat mir auch eine JSON-Datei geliefert, auf die ich aber nicht die korrekte Antwort geben konnte

Entsprechend wurde die Verbindung dann von der Gegenseite wieder geschlossen. Die in Chrome eingebaute Diagnoseseite hat leider in der Zeit nichts gezeigt:

chrome://net-internals/#sockets

Insofern sind Fiddler und die eingebauten Chromium-Debugger aber durchaus geeignete Werkzeuge.

Test mit ClientWebSocket

Im .Net-Framework gibt es auch eine fertige Klasse für die Websocket Kommunikation. Einen Beispiel-Code habe ich mangels Bedarf noch nicht geschrieben. Für einen klassischen "PowerShell-Entwickler", der eigentlich klassischen prozeduralen Code mit gelegentlichen Einsatz eigener Objekte schreibt, ist die asynchrone Verarbeitung von JavaScript, Node.JS und anderen Plattformen natürlich eine Herausforderung. Ein PowerShell-Script kann nicht einfach darauf warten, dass eine Information kommt. Ein klassisches "Ist was da"-Polling ist sehr ineffektiv.

Stattdessen sind Funktionen zum Verarbeiten der eingehenden Informationen zu definieren, die dann von der Klasse beim Eingang neuer Nachrichten aufgerufen werden. Das stelle ganz neue Herausforderungen an Debugging, Synchronisierung von Code, paralleler Verarbeitung und Threads

Cleanup

Wenn Sie viele Tests dieser Seite selbst ausprobiert haben, dann sollten Sie die dazu gestarteten Prozesse (Browser, PowerShell, etc.) einmal schließen. Die ganzen Websocket-Klassen laufen asynchron, d.h. Sie werden instanziert und bekommen dann eine Funktion für die verschiedenen Events zugewiesen. Die Verarbeitung läuft ggfls. im Hintergrund weiter. Ich habe das nur gesehen, dass ich nach dem Ende meiner Tests immer noch einen Wireshark-Mitschnitt aktiv hatte, der munter weiter Websocket-Pakete angezeigt hat. Über den Ressource Monitor konnte ich mit der Remote-IP-Adresse und Remote-Port den Prozess ausfindig machen. Bei mir war es ein Chrom-Browser, in dessen Debugger-Konsole ich wohl Websockets gestartet habe. Auch nach Beenden des Debuggers lief der Code weiter. Erst ein Neustart des Browsers hat dies letztlich beruhigt.

Weitere Links