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 padronizados do IETF.
  • 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.

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.

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. Só a porta 80 da aplicação recebe tráfego, 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

Saiba mais

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