sgcIndy 2026.6: Reforço de Segurança do Servidor para TLS, TCP e HTTP

· Lançamentos

O sgcIndy 2026.6 volta sua atenção para o lado do servidor. Esta versão adiciona um conjunto de opções de reforço de segurança opcional aos componentes de servidor TLS, TCP e HTTP, fechando uma série de classes de ataque bem conhecidas contra as quais o código Indy subjacente nunca se protegeu: um callback de verificação de certificado que falha de forma permissiva, conexões Slowloris de gotejamento lento, corpos e cabeçalhos de requisição ilimitados e HTTP request smuggling. Ela também entrega as primeiras primitivas de encapsulamento de chave pós-quântico ML-KEM-768.

Toda nova proteção assume por padrão o comportamento anterior, então as aplicações existentes não são afetadas até que você as ative. Este artigo percorre cada uma delas com trechos de código Delphi prontos para colar.

Servidores TLS reforçados

A correção mais importante está no callback de verificação de par do OpenSSL. No Indy original, o callback de verificação pode falhar de forma permissiva: quando um servidor solicita um certificado de cliente, a decisão final era conduzida pelo callback do usuário e ignorava o próprio veredito do OpenSSL, de modo que um callback que retornasse sucesso aceitava um certificado expirado, autoassinado ou não confiável. A nova flag TIdSSLOptions.StrictVerify impõe o resultado do OpenSSL, de modo que um handler personalizado OnVerifyPeer só pode restringir ainda mais a decisão, nunca afrouxá-la.

Mais três flags conectam opções do OpenSSL que os headers já declaravam, mas que a biblioteca nunca aplicava: DisableCompression mitiga o CRIME, DisableRenegotiation bloqueia a renegociação iniciada pelo cliente (um DoS assimétrico em CPU) no OpenSSL 1.1.0h e posteriores, e ServerCipherPreference faz com que a ordem de cifras do servidor vença a negociação em vez da do cliente.

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;

Defina VerifyMode como [sslvrfPeer, sslvrfFailIfNoPeerCert] junto com StrictVerify quando você quiser TLS mútuo que de fato rejeite um certificado de cliente desconhecido.

Detendo o Slowloris

Um cliente Slowloris abre muitas conexões e goteja um byte de cada vez, nunca completando a requisição, para travar cada thread de trabalho. A defesa natural parece ser um timeout de leitura, mas há uma sutileza que vale conhecer: o ReadTimeout do Indy é um timeout de inatividade. Cada byte que chega o reinicia, então um cliente que envia um byte a cada poucos segundos mantém a conexão viva para sempre.

O 2026.6 adiciona um verdadeiro prazo total de leitura ao TIdIOHandler via SetReadDeadline. Ele é verificado a cada leitura, então dispara mesmo enquanto os bytes continuam pingando. O servidor HTTP conecta isso automaticamente através da nova propriedade RequestReadTimeout, que limita o tempo para receber a linha de requisição e os cabeçalhos e é reiniciada a cada requisição 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;

Para um TIdTCPServer personalizado, você pode aplicar o mesmo prazo manualmente ao redor do seu laço de leitura. Passe 0 para limpá-lo.

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;

Limites de requisição para servidores HTTP

Um servidor HTTP que lê tudo o que um cliente declara é um alvo fácil de exaustão de memória. Uma única requisição pode anunciar Content-Length: 2000000000 e o servidor tentará bufferizar dois gigabytes, ou transmitir um corpo chunked infinito, ou enviar milhões de bytes de cabeçalho. O 2026.6 adiciona três limites e uma verificação de smuggling ao TIdCustomHTTPServer.

O MaxRequestBodySize limita o Content-Length e o corpo chunked somado e responde 413 Payload Too Large quando é excedido. O MaxHeaderTotalSize limita o total de bytes de cabeçalho e responde 431 Request Header Fields Too Large. O StrictRequestParsing rejeita requisições propositalmente ambíguas, como uma mensagem que carrega tanto Content-Length quanto Transfer-Encoding: chunked (o clássico vetor de request smuggling) ou um tamanho de chunk negativo, respondendo 400 Bad Request. O laço de cabeçalhos trailer do chunked agora também é limitado, então um atacante não pode mais manter uma conexão aberta com um fluxo infinito de linhas de trailer.

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 camada TCP bruta, o TIdIOHandler.MaxInputBufferSize limita o buffer de entrada acumulado para qualquer protocolo construído sobre o IOHandler, o que impede que uma leitura com prefixo de tamanho ou uma linha de tamanho excessivo façam o buffer crescer sem limite.

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

Opcional por design

Nenhuma dessas opções altera o comportamento padrão. Todos os campos assumem por padrão o estado desligado (0 para os limites de tamanho e timeout, False para as flags booleanas), então um projeto que atualiza para o 2026.6 se comporta exatamente como se comportava no 2026.5. Você habilita precisamente as proteções de que sua implantação precisa, e o mesmo código compila do Delphi 7 ao RAD Studio 13 e Free Pascal.

Também nesta versão

O 2026.6 introduz as primeiras primitivas de encapsulamento e desencapsulamento pós-quântico ML-KEM-768, disponíveis no OpenSSL 3.5 e posteriores. Elas expõem uma API TBytes simples para que você possa adicionar uma etapa de encapsulamento de chave pós-quântico a um handshake híbrido junto com a troca ECDH clássica.

No lado da compilação, a compilação de pacotes do C++Builder não falha mais com o erro do MSBuild MSB1008 quando o RAD Studio está instalado em um caminho que contém espaços. O parâmetro DCC_BpiOutput agora está entre aspas.

Atualizando

O 2026.6 é uma atualização direta. Não há mudanças que quebrem compatibilidade nem nada a migrar, porque toda nova proteção é opcional. Revise os trechos acima e habilite as opções que se ajustam ao seu servidor.

O sgcIndy é gratuito. Baixe a versão mais recente em esegece.com/products/sgcindy/download.

Dúvidas, feedback ou ajuda para reforçar seu servidor? Entre em contato, você receberá uma resposta das pessoas que escreveram o código.