Fortalecendo o Servidor HTTP no sgcWebSockets

· Componentes

Um servidor WebSocket quase sempre também fala HTTP: ele serve a página que abre o socket, responde verificações de integridade, hospeda uma API ou redireciona para o seu front end. Essa superfície HTTP tem suas próprias classes de ataque bem conhecidas, e a versão mais recente do sgcWebSockets fecha as mais importantes por padrão. Isso se aplica aos dois servidores HTTP: o TsgcWebSocketHTTPServer baseado em Indy e o TsgcWebSocketServer_HTTPAPI baseado no http.sys do Windows.

O path traversal foi vedado

Se você serve arquivos estáticos definindo o DocumentRoot, o servidor costumava montar o caminho do arquivo juntando o document root ao caminho da requisição. Uma requisição como GET /../../../../windows/win.ini, ou sua forma codificada em URL /..%2f..%2f..., podia sair da raiz web e ler arquivos arbitrários. O servidor agora canonicaliza o caminho resolvido e o serve somente quando ele permanece dentro do DocumentRoot; qualquer coisa que escape é rejeitada. Isso está sempre ativo, tanto no caminho HTTP/1.x quanto no HTTP/2, e não precisa de configuração.

Limitando o corpo da requisição

O corpo de uma requisição HTTP não tem limite de tamanho embutido, então um cliente poderia declarar um Content-Length enorme e transmitir gigabytes para esgotar a memória do servidor. A nova propriedade MaxRequestBodySize impõe esse limite. Ela tem como padrão 64 MB e uma requisição que declara mais é rejeitada com 413 antes que o corpo seja armazenado em buffer. Aumente o valor para uploads grandes, ou defina como 0 para desativar o limite.

oServer := TsgcWebSocketHTTPServer.Create(nil);
oServer.Port := 80;
oServer.MaxRequestBodySize := 16 * 1024 * 1024;  // 16 MB, reject larger with 413
oServer.Active := True;

Análise mais rígida da requisição impede o smuggling

O HTTP request smuggling abusa de servidores que discordam sobre onde uma requisição termina e a próxima começa, normalmente enviando tanto um cabeçalho Content-Length quanto um Transfer-Encoding: chunked. A nova propriedade StrictRequestParsing, habilitada por padrão, rejeita essas requisições ambíguas com 400 e aplica uma validação mais rígida de chunked-encoding, de modo que um proxy de front-end e o servidor não possam ser dessincronizados.

oServer := TsgcWebSocketHTTPServer.Create(nil);
oServer.StrictRequestParsing := True;   // default: reject Content-Length + Transfer-Encoding
oServer.MaxRequestBodySize := 64 * 1024 * 1024;
oServer.Active := True;

Defesa contra o HTTP/2 Rapid Reset

O ataque HTTP/2 Rapid Reset (CVE-2023-44487) abre um stream e o cancela imediatamente com RST_STREAM, em um laço fechado, forçando o servidor a realizar trabalho de requisição ilimitado a um custo quase nulo para o cliente. O servidor HTTP/2 agora limita quantos resets e quadros de controle uma única conexão pode enviar, traz um padrão finito de 100 streams concorrentes e fecha uma conexão abusiva com um quadro GOAWAY. O HTTP/2 é opt-in, então isso só importa se você o habilitou, mas, se o fez, a defesa é automática.

Cabeçalhos de resposta mais limpos

Quando uma aplicação monta um cabeçalho de resposta a partir de dados influenciados pela requisição, um destino de redirecionamento ou uma entity tag, por exemplo, um carriage-return ou line-feed introduzido nesse valor poderia dividir a resposta e injetar cabeçalhos extras. O servidor http.sys agora remove CR e LF dos valores de cabeçalho de resposta como Location, Server e ETag, de modo que um único valor não possa mais se tornar dois cabeçalhos.

Onde o firewall se encaixa

Essas proteções ficam no próprio parser HTTP e no servidor de arquivos do servidor, que é a camada que os componentes Firewall e RateLimiter não conseguem alcançar. Continue usando o firewall para filtragem de IP, limites de taxa e política de origem, e deixe as defesas embutidas protegerem a análise e o serviço de arquivos estáticos. Para um servidor público, também recomendamos definir o DocumentRoot somente quando você realmente serve arquivos, ajustar o MaxRequestBodySize ao seu máximo real e limitar o MaxConnections.

Atualizando

As novas proteções ficam ativas assim que você atualiza, com padrões seguros, então a maioria dos servidores ganha o fortalecimento sem mudança de código. Se a sua aplicação aceita corpos de requisição maiores que 64 MB, aumente o MaxRequestBodySize conforme necessário. Os clientes existentes não são afetados.

Atualize a partir da página de download do sgcWebSockets, ou obtenha-o através do GetIt ou da sua conta registrada.

Dúvidas, feedback ou ajuda com a migração? Entre em contato, você receberá uma resposta das pessoas que escreveram o código.