Wzmacnianie zabezpieczeń serwera WebSocket w sgcWebSockets

· Komponenty

Serwer WebSocket wystawiony na publiczny internet jest kuszącym celem. Protokół nie ma wbudowanego limitu rozmiaru wiadomości, więc pojedyncze połączenie może próbować pociągnąć cały proces w dół za pomocą jednej spreparowanej ramki, a uścisk dłoni przy zmianie protokołu jest łatwym miejscem do sondowania niedbałej walidacji. Najnowszy sgcWebSockets dostarcza zestaw zabezpieczeń, które domyślnie zamykają te drzwi, dzięki czemu Twój serwer pozostaje aktywny nawet pod wrogim ruchem.

Wszystko poniżej dotyczy wszystkich trzech serwerów WebSocket: TsgcWebSocketServer, TsgcWebSocketHTTPServer oraz opartego na http.sys TsgcWebSocketServer_HTTPAPI. Edycja .NET dziedziczy te same zabezpieczenia, ponieważ działa na tej samej natywnej bibliotece.

Jeden limit, który powstrzymuje trzy ataki na pamięć

Sztandarowym dodatkiem jest pojedyncza właściwość, MaxMessageSize, która ogranicza maksymalny rozmiar wiadomości przychodzącej. Domyślnie wynosi 64 MB, co jest hojną wartością dla niemal każdej aplikacji, a Ty możesz ją zwiększyć lub zmniejszyć albo ustawić na 0, aby wyłączyć limit.

Ta jedna właściwość chroni przed trzema różnymi technikami wyczerpywania pamięci, które wszystkie kończą się tak samo, czyli wyczerpaniem pamięci RAM serwera:

W każdym przypadku połączenie jest zamykane w sposób czysty z kodem zamknięcia WebSocket 1009 (Message Too Big), a pamięć serwera nigdy nie przekracza limitu.

oServer := TsgcWebSocketServer.Create(nil);
oServer.Port := 80;
// accept messages up to 16 MB, reject anything larger with close 1009
oServer.MaxMessageSize := 16 * 1024 * 1024;
oServer.Active := True;

Bezpieczne parsowanie długości ramki

Ramki WebSocket mogą nieść 64-bit pole długości. Zgodnie z RFC 6455 najbardziej znaczący bit tego pola musi wynosić zero. sgcWebSockets odrzuca teraz ramkę, której 64-bit długość ma ustawiony najwyższy bit, zamiast jej ufać, dzięki czemu powyższego limitu rozmiaru nie da się ominąć liczbą całkowitą, która się zawija. Ta kontrola jest zawsze włączona i nie zależy od MaxMessageSize.

Bardziej rygorystyczne uściski dłoni

Dwie nowe opcje w ramach SecurityOptions wzmacniają samą zmianę protokołu WebSocket, a obie są domyślnie włączone.

EnforceWebSocketVersion odpowiada na każdy uścisk dłoni, który prosi o wersję inną niż 13, kodem 426 Upgrade Required oraz nagłówkiem Sec-WebSocket-Version: 13, zamiast dokończyć zmianę protokołu. ValidateWebSocketKey odrzuca, kodem 400 Bad Request, każdy uścisk dłoni, którego Sec-WebSocket-Key jest brakujący lub nie jest prawidłowym 16-bajtowym nonce w formacie base64. Obie kontrole dotyczą wyłącznie ścieżki RFC 6455, więc starsze klienty oparte na dotychczasowych specyfikacjach nie są nimi dotknięte.

oServer := TsgcWebSocketServer.Create(nil);
oServer.SecurityOptions.EnforceWebSocketVersion := True;  // 426 on a wrong version
oServer.SecurityOptions.ValidateWebSocketKey := True;     // 400 on a malformed key
// lock the server to your own site while you are at it
oServer.SecurityOptions.OriginsAllowed := 'https://app.example.com';
oServer.Active := True;

Jak to współgra z zaporą

Te zabezpieczenia działają na warstwie protokołu, wewnątrz parsera ramek, czyli dokładnie tam, gdzie dochodzi do ataków na pamięć. To ta część, do której komponenty Firewall i RateLimiter nie sięgają, ponieważ widzą wiadomość dopiero po jej zdekodowaniu. Obie warstwy uzupełniają się nawzajem: nadal używaj zapory i ogranicznika przepustowości do filtrowania adresów IP, limitów liczby połączeń i wiadomości oraz polityki pochodzenia, a nowym wbudowanym limitom pozwól strzec samego parsera. Dla publicznego serwera zalecamy ustawienie MaxMessageSize na rzeczywiste maksimum, zablokowanie OriginsAllowed na Twój front-end oraz ograniczenie MaxConnections.

Aktualizacja

Nowe zabezpieczenia są aktywne, gdy tylko dokonasz aktualizacji, z bezpiecznymi wartościami domyślnymi, więc większość serwerów zyskuje wzmocnienie pamięci i uścisku dłoni bez ani jednej zmiany w kodzie. Jeśli Twoja aplikacja zgodnie z przeznaczeniem wymienia wiadomości większe niż 64 MB, odpowiednio zwiększ MaxMessageSize. Istniejący kod klienta pozostaje nienaruszony.

Zaktualizuj z strony pobierania sgcWebSockets lub pobierz przez GetIt albo swoje zarejestrowane konto.

Pytania, opinie lub pomoc w migracji? Skontaktuj się z nami, a otrzymasz odpowiedź od osób, które napisały ten kod.