sgcIndy 2026.6: Wzmocnienie bezpieczeństwa serwera dla TLS, TCP i HTTP

· Wydania

sgcIndy 2026.6 skupia uwagę na stronie serwerowej. To wydanie dodaje zestaw opcjonalnych opcji wzmocnienia bezpieczeństwa do komponentów serwerowych TLS, TCP i HTTP, zamykając szereg dobrze znanych klas ataków, przed którymi bazowy kod Indy nigdy nie chronił: wywołanie zwrotne weryfikacji certyfikatu działające w trybie fail-open, powolne połączenia Slowloris, nieograniczone treści i nagłówki żądań oraz przemyt żądań HTTP. Dostarcza również pierwsze prymitywy postkwantowej enkapsulacji klucza ML-KEM-768.

Każda nowa ochrona domyślnie zachowuje poprzednie działanie, więc istniejące aplikacje pozostają nienaruszone, dopóki jej nie włączysz. Ten wpis omawia kolejno każdą z nich wraz z gotowymi do wklejenia fragmentami kodu w Delphi.

Wzmocnione serwery TLS

Najważniejsza poprawka dotyczy wywołania zwrotnego weryfikacji partnera w OpenSSL. W standardowym Indy wywołanie zwrotne weryfikacji może działać w trybie fail open: gdy serwer żąda certyfikatu klienta, ostateczna decyzja była sterowana wywołaniem zwrotnym użytkownika i ignorowała własny werdykt OpenSSL, więc wywołanie zwrotne, które zwracało sukces, akceptowało certyfikat wygasły, podpisany samodzielnie lub niezaufany. Nowa flaga TIdSSLOptions.StrictVerify wymusza wynik OpenSSL, więc niestandardowy obsługujący OnVerifyPeer może decyzję jedynie dalej zaostrzyć, nigdy złagodzić.

Trzy kolejne flagi podłączają opcje OpenSSL, które nagłówki już deklarowały, ale biblioteka nigdy ich nie stosowała: DisableCompression ogranicza CRIME, DisableRenegotiation blokuje renegocjację inicjowaną przez klienta (atak DoS o asymetrycznym obciążeniu CPU) w OpenSSL 1.1.0h i nowszych, a ServerCipherPreference sprawia, że to kolejność szyfrów serwera wygrywa negocjację zamiast kolejności klienta.

uses
  IdHTTPServer, IdSSLOpenSSL;

var
  oServer: TIdHTTPServer;
  oSSL: TIdServerIOHandlerSSLOpenSSL;
begin
  oServer := TIdHTTPServer.Create(nil);
  oSSL := TIdServerIOHandlerSSLOpenSSL.Create(oServer);

  oSSL.SSLOptions.CertFile    := 'server.pem';
  oSSL.SSLOptions.KeyFile     := 'server.key';
  oSSL.SSLOptions.SSLVersions := [sslvTLSv1_2, sslvTLSv1_3];

  // opt-in hardening, every flag defaults to False
  oSSL.SSLOptions.StrictVerify           := True;  // enforce the OpenSSL verdict
  oSSL.SSLOptions.DisableCompression     := True;  // CRIME
  oSSL.SSLOptions.DisableRenegotiation   := True;  // renegotiation DoS
  oSSL.SSLOptions.ServerCipherPreference := True;  // server picks the cipher

  oServer.IOHandler := oSSL;
  oServer.Active := True;
end;

Ustaw VerifyMode na [sslvrfPeer, sslvrfFailIfNoPeerCert] razem z StrictVerify, gdy chcesz mieć wzajemne TLS, które faktycznie odrzuca nieznany certyfikat klienta.

Zatrzymanie Slowloris

Klient Slowloris otwiera wiele połączeń i przesyła po jednym bajcie naraz, nigdy nie kończąc żądania, aby zablokować każdy wątek roboczy. Naturalna obrona wygląda jak limit czasu odczytu, ale jest tu subtelność warta poznania: ReadTimeout w Indy to limit czasu bezczynności. Każdy przybywający bajt go resetuje, więc klient, który wysyła bajt co kilka sekund, utrzymuje połączenie przy życiu w nieskończoność.

2026.6 dodaje prawdziwy całkowity termin odczytu do TIdIOHandler za pomocą SetReadDeadline. Jest sprawdzany przy każdym odczycie, więc zadziała nawet wtedy, gdy bajty wciąż napływają kropla po kropli. Serwer HTTP podłącza go automatycznie poprzez nową właściwość RequestReadTimeout, która ogranicza czas na odebranie wiersza żądania i nagłówków oraz resetuje się dla każdego żądania keep-alive.

uses
  IdHTTPServer;

var
  oServer: TIdHTTPServer;
begin
  oServer := TIdHTTPServer.Create(nil);
  oServer.DefaultPort := 8080;

  // close any client that has not finished sending the request
  // line and headers within 5 seconds, even if it keeps dripping bytes
  oServer.RequestReadTimeout := 5000;

  oServer.Active := True;
end;

W przypadku niestandardowego TIdTCPServer możesz zastosować ten sam termin ręcznie wokół swojej pętli odczytu. Przekaż 0, aby go wyczyścić.

procedure TForm1.ServerExecute(AContext: TIdContext);
var
  vLine: string;
begin
  // bound the whole request to 5 seconds of total read time
  AContext.Connection.IOHandler.SetReadDeadline(5000);
  try
    vLine := AContext.Connection.IOHandler.ReadLn;
    // ... handle the request ...
  finally
    AContext.Connection.IOHandler.SetReadDeadline(0);
  end;
end;

Limity żądań dla serwerów HTTP

Serwer HTTP, który odczytuje wszystko, co deklaruje klient, jest łatwym celem wyczerpania pamięci. Pojedyncze żądanie może ogłosić Content-Length: 2000000000, a serwer spróbuje buforować dwa gigabajty, albo strumieniować nieskończoną treść fragmentowaną, albo wysłać miliony bajtów nagłówka. 2026.6 dodaje trzy limity oraz kontrolę przemytu do TIdCustomHTTPServer.

MaxRequestBodySize ogranicza Content-Length oraz zsumowaną treść fragmentowaną i odpowiada 413 Payload Too Large, gdy zostanie przekroczony. MaxHeaderTotalSize ogranicza całkowitą liczbę bajtów nagłówków i odpowiada 431 Request Header Fields Too Large. StrictRequestParsing odrzuca żądania, które są celowo niejednoznaczne, takie jak komunikat zawierający jednocześnie Content-Length i Transfer-Encoding: chunked (klasyczny wektor przemytu żądań) lub ujemny rozmiar fragmentu, odpowiadając 400 Bad Request. Pętla nagłówków końcowych (trailer) treści fragmentowanej jest teraz również ograniczona, więc atakujący nie może już utrzymywać połączenia otwartego nieskończonym strumieniem wierszy końcowych.

oServer.MaxRequestBodySize := 10 * 1024 * 1024;  // 10 MB, else 413
oServer.MaxHeaderTotalSize := 64 * 1024;         // 64 KB, else 431
oServer.StrictRequestParsing := True;            // reject CL+TE smuggling, else 400

Na surowej warstwie TCP TIdIOHandler.MaxInputBufferSize ogranicza skumulowany bufor wejściowy dla dowolnego protokołu zbudowanego na IOHandler, co powstrzymuje odczyt z prefiksem długości lub zbyt duży wiersz przed nieograniczonym powiększaniem bufora.

// inside OnConnect / OnExecute of any Indy server
AContext.Connection.IOHandler.MaxInputBufferSize := 1024 * 1024;  // 1 MB cap

Opcjonalne z założenia

Żadna z tych opcji nie zmienia domyślnego działania. Wszystkie pola są domyślnie wyłączone (0 dla limitów rozmiaru i czasu, False dla flag logicznych), więc projekt, który aktualizuje się do 2026.6, zachowuje się dokładnie tak samo jak na 2026.5. Włączasz dokładnie te zabezpieczenia, których potrzebuje Twoje wdrożenie, a ten sam kod kompiluje się od Delphi 7 po RAD Studio 13 oraz Free Pascal.

Również w tym wydaniu

2026.6 wprowadza pierwsze prymitywy postkwantowej enkapsulacji i dekapsulacji ML-KEM-768, dostępne w OpenSSL 3.5 i nowszych. Udostępniają proste API TBytes, dzięki czemu możesz dodać postkwantowy krok enkapsulacji klucza do hybrydowego uzgadniania obok klasycznej wymiany ECDH.

Po stronie kompilacji, kompilacja pakietów C++Builder nie kończy się już błędem MSBuild MSB1008, gdy RAD Studio jest zainstalowane w ścieżce zawierającej spacje. Parametr DCC_BpiOutput jest teraz ujęty w cudzysłów.

Aktualizacja

2026.6 to aktualizacja typu drop-in. Nie ma żadnych zmian łamiących zgodność ani niczego do migracji, ponieważ każda nowa ochrona jest opcjonalna. Przejrzyj powyższe fragmenty kodu i włącz opcje pasujące do Twojego serwera.

sgcIndy jest darmowe. Pobierz najnowszą wersję z esegece.com/products/sgcindy/download.

Pytania, opinie lub pomoc we wzmocnieniu bezpieczeństwa Twojego serwera? Skontaktuj się z nami, otrzymasz odpowiedź od osób, które napisały ten kod.