Como proteger seu site ou bot contra DDoS

A proteção contra DDoS tem duas camadas: um provedor que absorve o tráfego volumétrico antes de ele chegar ao servidor e uma aplicação que não gasta recursos com requisições abusivas. Você configura a segunda.

Atualizado em 6 min de leitura

Objetivos de aprendizado

Depois de ler este artigo, você vai conseguir:

  • Entender o que um provedor filtra e o que continua sendo problema da sua aplicação
  • Configurar limite de requisições, timeouts e limite de corpo em uma API Node.js
  • Testar com curl se a proteção está funcionando
Nesta página

Um ataque DDoS (negação de serviço distribuída) usa muitas máquinas ao mesmo tempo para deixar um alvo sem resposta. Segundo a CISA, os atacantes costumam usar botnets, redes de dispositivos sequestrados. O efeito para você é simples: usuários legítimos não conseguem acessar o site, a API ou o bot.

Não existe proteção total só com código. Parte do ataque precisa ser filtrada antes de chegar ao seu servidor, e outra parte você resolve na própria aplicação. Os exemplos abaixo mostram o que cada camada faz e como configurar a sua, usando uma API em Node.js.

TráfegoBordaCDN e WAFL3, L4 e L7Aplicaçãorate limite timeoutsCódigoVolumétricoProtocoloCamada 7429Usuárioslegítimos
A borda barra o volume e os ataques de protocolo; o que passa, a aplicação contém com rate limit e timeouts.

Que tipos de ataque DDoS existem?#

Três famílias cobrem quase tudo:

  • Volumétrico: inunda o link de rede com tráfego (camadas 3 e 4 do modelo OSI). Seu código nem chega a ser executado, porque a banda acaba antes.
  • De protocolo: abusa do funcionamento do TCP, como milhares de conexões semiabertas, para esgotar tabelas de estado do servidor ou do firewall.
  • De aplicação (camada 7): envia requisições HTTP aparentemente legítimas, como milhares de buscas por segundo numa rota pesada. É o mais barato de lançar e o que mais depende de você.

Essa divisão decide a defesa. Os dois primeiros só quem tem muita capacidade de rede consegue absorver. O terceiro você reduz com cache, limites e código eficiente.

O que seu provedor faz e o que sobra para você?#

A Cloudflare, por exemplo, descreve mitigação automática nas camadas 3, 4 e 7 aplicada na borda da rede. Esse é o modelo comum: uma CDN ou um proxy reverso recebe todo o tráfego, descarta o que reconhece como ataque e repassa o resto.

Mesmo com isso, o que passa pelo filtro ainda chega ao seu código. Sobram para você:

  • limitar quantas requisições cada cliente faz;
  • fechar conexões lentas ou paradas;
  • recusar corpos de requisição enormes;
  • cachear respostas caras;
  • não deixar a aplicação acessível por um caminho que ignore a borda.

Como limitar requisições por cliente?#

O rate limit responde 429 (Too Many Requests) quando um cliente passa do limite em uma janela de tempo, como descreve a MDN. Em Express, o pacote express-rate-limit faz isso. Os exemplos usam uma versão recente do Node.js e Express 5.

bash
npm install express express-rate-limit
js
// server.js
import express from "express";
import { rateLimit } from "express-rate-limit";

const app = express();

// Atrás de 1 proxy (CDN ou balanceador), use o IP real do cliente.
// Ajuste o número à sua realidade: veja a seção sobre trust proxy.
app.set("trust proxy", 1);

// Limite global: 100 requisições por minuto por IP.
app.use(
  rateLimit({
    windowMs: 60 * 1000,
    limit: 100,
    standardHeaders: "draft-7",
    legacyHeaders: false,
  }),
);

// Rotas sensíveis têm limite mais baixo.
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  limit: 10,
  standardHeaders: "draft-7",
  legacyHeaders: false,
});

// Corpo JSON limitado a 100 KB.
app.use(express.json({ limit: "100kb" }));

app.get("/", (req, res) => res.json({ ok: true }));
app.post("/login", loginLimiter, (req, res) => res.json({ ok: true }));

const server = app.listen(process.env.PORT ?? 3000);

// Corta conexões lentas (slowloris) e requisições que nunca terminam.
server.headersTimeout = 15_000;
server.requestTimeout = 30_000;

Pontos que valem atenção:

  • Na documentação do pacote, limit é o máximo por janela e windowMs o tamanho da janela em milissegundos. O padrão da biblioteca é limit: 5 por minuto, então sempre defina o seu.
  • legacyHeaders: false desliga os cabeçalhos X-RateLimit-* antigos, e standardHeaders liga os cabeçalhos RateLimit do rascunho do IETF (draft-7 é uma das versões aceitas).
  • O contador fica na memória do processo por padrão. Com várias instâncias, cada uma conta separado. Para um limite compartilhado, use um store externo, como o Redis.

Por que o trust proxy importa tanto?#

Atrás de um proxy, req.ip seria o endereço do proxy, e o rate limit trataria todos os usuários como um só: um cliente abusivo bloquearia todo mundo. O trust proxy diz ao Express em quais valores do cabeçalho X-Forwarded-For confiar, conforme o guia do Express. Com um número, ele conta os saltos a partir da aplicação: 1 confia só no proxy da frente e usa o último IP do cabeçalho, o que esse proxy anotou.

O erro oposto também é perigoso. Se você confia em mais saltos do que existem de verdade, o cliente forja o cabeçalho e escolhe o IP que quiser, o que derruba o rate limit. Por isso:

  1. Descubra quantos proxies existem entre o usuário e a sua aplicação.
  2. Use esse número em trust proxy, e não true, a menos que o último proxy sobrescreva o cabeçalho.
  3. Confirme registrando req.ip em uma rota de teste, a partir de dois IPs diferentes.
X-Forwarded-Forconexão TCP203.0.113.9o cliente forjou198.51.100.7a CDN anotouIP da CDNendereço de origemtrust proxyreq.ipResultadofalseIP da CDNTodos parecemum só cliente1198.51.100.7IP realdo clientetrue203.0.113.9IP que oatacante quis
Com trust proxy igual ao número real de proxies, req.ip é o cliente; false junta todo mundo e true aceita o IP forjado.

Quais timeouts configurar?#

Um ataque de conexão lenta mantém sockets abertos enviando poucos bytes por vez. A documentação do Node.js diz que server.headersTimeout e server.requestTimeout devem ter valor diferente de zero para proteger contra negação de serviço quando o servidor não está atrás de um proxy reverso. O padrão do requestTimeout é 300 segundos e o do headersTimeout é o menor valor entre requestTimeout e 60 segundos. No código acima eles caem para 15 e 30 segundos. O Node verifica esses prazos a cada 30 segundos por padrão (connectionsCheckingInterval), então o corte pode vir um pouco depois do valor configurado.

Se sua API recebe uploads grandes, 30 segundos para o corpo inteiro pode ser pouco. Nesse caso, mantenha um requestTimeout mais alto e aceite uploads por um serviço separado.

Como reduzir o custo de cada requisição?#

No ataque de camada 7, o dano é proporcional ao quanto cada requisição custa. Três ajustes baratos:

  • Cache: sirva páginas e respostas públicas com Cache-Control para que a borda responda sem chamar o seu código. Veja o que é Cache-Control.
  • Rotas pesadas atrás de autenticação: buscas, exportações e geração de relatórios não precisam estar abertas ao mundo.
  • Paginação e limites: nunca devolva uma tabela inteira nem aceite limit=1000000.

O restante do endurecimento da API, como validação de entrada e logs, está no checklist de produção de uma API.

Como testar se funciona?#

Com o servidor rodando, dispare 130 requisições seguidas e conte os códigos de resposta:

bash
for i in $(seq 1 130); do
  curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3000/
done | sort | uniq -c

A saída esperada com limit: 100, se você rodar logo após iniciar o servidor, é esta:

text
    100 200
     30 429

Para o timeout de cabeçalhos, envie uma requisição incompleta (sem a linha em branco final) e espere:

bash
(printf 'GET / HTTP/1.1\r\nHost: localhost\r\n'; sleep 60) | nc localhost 3000
  # o servidor responde 408 Request Timeout e fecha a conexão

Em produção, o teste real é observar os logs: muitos 429 vindos de poucos IPs mostram o limite agindo, e picos de tráfego em rotas inesperadas indicam o alvo do ataque.

Quais erros são comuns?#

  • Rate limit sem trust proxy: todos os usuários compartilham o IP do proxy e são bloqueados juntos.
  • trust proxy: true sem necessidade: o cliente forja o IP e escapa do limite.
  • Aplicação acessível pelo IP direto: o atacante ignora a CDN e bate direto no servidor. Responda só pelo domínio ou aceite tráfego apenas da borda.
  • Limite igual para tudo: login e busca precisam de limites bem menores que arquivos estáticos.
  • Contagem em memória com várias instâncias: o limite real vira N vezes o configurado.

Como fazer isso na Square Cloud#

Na Square Cloud, a primeira camada vem em todos os planos: mitigação de DDoS nas camadas 3, 4 e 7 e um WAF na frente das aplicações, numa rede com CDN Anycast de mais de 200 pontos de presença (baseada na Cloudflare) e até 10 Gbps por servidor. O único tráfego de entrada chega pelo endereço web da aplicação, publicado por HTTPS no subdomínio squareweb.app ou no seu domínio. Nenhuma mitigação torna um serviço imune, e a segunda camada continua sendo sua, porque o código é seu.

Os exemplos deste artigo se encaixam assim. A CDN respeita o Cache-Control da sua aplicação, então as respostas públicas que você marcar como cacheáveis saem da borda sem chamar o seu código, e o purge de cache apaga o cache do host quando você publica uma mudança. Confirme o valor de trust proxy com o teste de req.ip a partir de dois IPs, como na seção sobre ele. Se você distribuir um domínio entre várias aplicações com load balancing (a partir do Standard), leve o contador do rate limit para a hospedagem de Redis, no mesmo datacenter, para o limite não virar N vezes o configurado.

Para acompanhar um ataque, o analytics web incluído em todo plano mostra rotas, países, status codes e bots verificados: um pico de 429 numa rota mostra o limite agindo. Veja o que a rede inclui na página de rede e conheça a hospedagem de Redis.

Infraestrutura

Mitigação de DDoS L3/L4/L7 e WAF em todos os planos

Rede com proteção DDoS

Perguntas frequentes

Dá para se proteger de DDoS sozinho, sem provedor?

Só de ataques pequenos. Um ataque volumétrico satura o link de rede antes de chegar ao seu código, então a mitigação precisa acontecer na borda, em quem tem capacidade de rede para absorver o tráfego.

Rate limit por IP resolve DDoS?

Resolve abuso de um IP ou de poucos, mas não um ataque distribuído com milhares de IPs. Ele é uma camada da defesa, junto com cache, WAF e mitigação no provedor.

Meu bot do Discord precisa de proteção contra DDoS?

Em geral o risco é baixo, porque o bot abre conexões de saída para o Gateway e não recebe tráfego público. O ponto de atenção é qualquer porta ou webhook exposto e o IP da sua máquina, se o bot roda em casa.

Como saber se estou sob ataque DDoS?

Sinais comuns são lentidão súbita, picos de requisições vindas de muitos IPs para as mesmas rotas e consumo alto de CPU ou muitas conexões abertas sem aumento de usuários reais. Confirme olhando os logs de acesso.

Próximo artigoChave de API exposta no GitHub: como revogar e prevenirSubiu uma chave de API para o GitHub? Revogue primeiro, depois limpe o histórico e troque o valor. Veja o passo a passo e como evitar que se repita.

Mais em Segurança

Ver todos os 10 artigos