← Voltar para o Blog

DDoS: o que dá para fazer antes de contratar mitigação

Nem todo pico de tráfego é ataque, e nem todo ataque exige serviço especializado. O que separa os casos, e as medidas que você aplica hoje com o que já tem.

Equipe EasyOps Cloud · · 8 min de leitura

O site ficou fora do ar por trinta minutos, o tráfego estava dez vezes acima do normal, e a primeira hipótese é ataque. Às vezes é. Com frequência parecida, é uma campanha que funcionou melhor que o esperado, um robô de indexação mal comportado, ou uma integração de cliente com laço infinito.

A distinção importa porque as respostas são diferentes — e porque a reação instintiva, contratar mitigação, resolve uma fatia menor dos casos do que se imagina.

Primeiro: é ataque mesmo?

Alguns sinais separam os cenários com razoável confiança.

Distribuição de origem. Tráfego legítimo vem de muitos IPs com padrão geográfico coerente com o seu público. Ataque volumétrico costuma vir de faixas inesperadas, e ataque simples costuma se concentrar em poucos endereços.

sudo awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

Comportamento. Pessoa carrega a página e os recursos dela, permanece um tempo, navega. Ataque costuma bater na mesma URL repetidamente, sem carregar imagem nem CSS, sem cookie e sem sequência.

sudo awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
sudo awk -F'"' '{print $6}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head

Um User-Agent respondendo por 60% das requisições é resposta suficiente.

Curva. Campanha sobe em minutos e desce devagar. Ataque sobe instantaneamente, mantém patamar e cai de uma vez.

Proporção de erro. Tráfego legítimo tem taxa de erro baixa. Ataque de aplicação costuma gerar muito 404 ou muito POST em endpoint de autenticação.

Os tipos, porque exigem respostas diferentes

TipoO que saturaOnde se resolve
VolumétricoA banda do linkSó na rede, antes de você
ConexãoTabela de conexões, portasFirewall e sistema
AplicaçãoCPU, banco, workersSua configuração e código

Essa tabela é a parte mais útil deste texto. Se o ataque satura o link, nada que você faça na máquina resolve — o tráfego já consumiu a banda antes de chegar. Aí a resposta é mitigação na rede, e ponto.

Os outros dois, que são os mais comuns em PME, você resolve com o que já tem.

Camada de conexão

Limitar conexões simultâneas por origem contém o ataque que abre milhares de conexões e as mantém abertas:

http {
    limit_conn_zone $binary_remote_addr zone=porip:10m;

    server {
        limit_conn porip 20;
        client_body_timeout 10s;
        client_header_timeout 10s;
        send_timeout 10s;
        keepalive_timeout 30s;
    }
}

Os timeouts curtos são a defesa contra ataque de conexão lenta, que consome recurso mantendo requisições incompletas abertas. O padrão do nginx é generoso demais para esse cenário.

No sistema, o rastreamento de conexões e as portas efêmeras são os recursos que esgotam primeiro — assunto dos artigos sobre NAT e too many open files:

ss -s
sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
# /etc/sysctl.d/99-rede.conf
net.ipv4.tcp_syncookies = 1
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 4096
net.netfilter.nf_conntrack_max = 262144

O tcp_syncookies é a proteção clássica contra inundação de abertura de conexão, e costuma já vir ativo.

Camada de aplicação, que é onde mais dói

Ataque de aplicação é o mais eficiente contra site pequeno, porque não exige volume: algumas centenas de requisições por segundo em uma busca pesada derrubam um servidor que aguentaria dez mil requisições em página estática.

http {
    limit_req_zone $binary_remote_addr zone=geral:10m rate=30r/s;
    limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;

    server {
        limit_req zone=geral burst=60 nodelay;

        location /login {
            limit_req zone=login burst=3;
            proxy_pass http://app;
        }

        location /busca {
            limit_req zone=geral burst=10;
            proxy_pass http://app;
        }
    }
}

O burst permite rajada curta sem punir usuário legítimo; o nodelay atende a rajada imediatamente em vez de enfileirar. Sem burst, o limite bloqueia navegação normal — a pessoa que abre três abas parece ataque.

Endpoints de autenticação merecem limite bem mais apertado, porque ali o objetivo raramente é derrubar: é testar credencial.

Vale medir antes de escolher o número. Um limite abaixo do tráfego normal transforma a proteção em indisponibilidade auto-infligida:

sudo awk '{print $4}' /var/log/nginx/access.log | uniq -c | sort -rn | head -5

Cache, que é a defesa mais subestimada

Se a resposta pode ser servida do cache, o ataque deixa de alcançar a aplicação e o banco — e passa a bater em algo que o nginx entrega com custo próximo de zero.

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=site:50m
                 max_size=2g inactive=60m;

location / {
    proxy_cache site;
    proxy_cache_valid 200 5m;
    proxy_cache_use_stale error timeout updating http_500 http_502 http_503;
    proxy_cache_lock on;
    add_header X-Cache $upstream_cache_status;
    proxy_pass http://app;
}

Duas diretivas fazem o trabalho pesado sob ataque. O proxy_cache_use_stale serve conteúdo vencido quando o backend está em dificuldade — melhor uma página de cinco minutos atrás que uma página de erro. E o proxy_cache_lock garante que, ao expirar, apenas uma requisição vá ao backend enquanto as outras aguardam, em vez de mil requisições simultâneas derrubarem o que restou.

Mesmo cinco minutos de cache em página dinâmica mudam completamente a capacidade de absorver pico.

Bloquear o que já foi identificado

Com o padrão identificado, o bloqueio é direto:

# Por origem
sudo ufw insert 1 deny from 203.0.113.0/24

# Por comportamento, com fail2ban lendo o log do nginx
# /etc/fail2ban/jail.local
[nginx-limit-req]
enabled = true
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 10
findtime = 60
bantime = 3600

Esse arranjo é elegante: o nginx registra quem estourou o limite, e o fail2ban bane essas origens no firewall — tirando o custo do processamento inteiro.

Para bloqueio por User-Agent, quando o padrão é claro:

if ($http_user_agent ~* (badbot|scrapy|masscan)) { return 444; }

O código 444 fecha a conexão sem responder nada, o que consome menos que devolver uma página de erro.

Quando isso não basta

Há um limite claro para o que se resolve no servidor. Se a banda do link está saturada, o tráfego já chegou e já custou — nenhuma regra local desfaz isso.

Os sinais de que passou desse ponto: o servidor está ocioso e mesmo assim inacessível; a latência de rede até ele explodiu; o volume está na casa de gigabits.

Aí as saídas são mitigação na rede do provedor, ou uma CDN que absorve o volume antes de chegar até você. Vale saber, antes de precisar, qual é o caminho para acionar isso — o suporte em português e no mesmo fuso é a diferença entre resolver em minutos ou em horas.

O que fazer antes de precisar

As medidas que valem estar prontas:

  • Rate limiting configurado com números medidos, não chutados.
  • Cache ativo, ainda que com validade curta.
  • Timeouts curtos de conexão.
  • `fail2ban` lendo o log do nginx.
  • Saber o tráfego normal. Sem linha de base, não há como reconhecer anomalia — e o vnstat do artigo sobre egress já dá esse histórico.
  • Um caminho de escalada definido, com quem acionar e como.

Nenhuma exige contratar nada, e todas continuam úteis fora de ataque: cache melhora desempenho, rate limiting protege contra integração mal escrita, e timeout curto evita acumular conexão morta. Configurar em dia tranquilo é bem mais barato que descobrir a sintaxe com o site fora do ar.

Preparar a comunicação, não só a técnica

Um aspecto que costuma faltar no plano e que pesa muito na percepção do incidente.

Durante um ataque, a pergunta que chega de todos os lados é "o que está acontecendo?". Sem resposta preparada, o time técnico gasta na comunicação o tempo que deveria gastar na mitigação — e a ausência de informação gera mais pressão que o próprio problema.

Três coisas prontas resolvem:

Uma página de status fora da sua infraestrutura. Se ela estiver no mesmo servidor atacado, ela cai junto — o que é o pior momento possível para descobrir isso. Hospedá-la em outro lugar custa quase nada.

Um texto padrão que possa ser adaptado em dois minutos, dizendo o que se sabe, o que está sendo feito e quando haverá nova atualização.

Uma pessoa responsável por comunicar, diferente de quem está mitigando. Parece excesso para equipe pequena, e é justamente em equipe pequena que a mesma pessoa acaba fazendo as duas coisas mal.

Vale também definir de antemão o critério de escalada: a partir de qual duração ou volume o caso deixa de ser tratado internamente e vira acionamento do provedor. Essa decisão tomada com calma economiza a hora que se perde discutindo enquanto o site está fora.

Depois que passar

O incidente encerrado é o melhor momento para melhorar, porque os dados estão frescos e a motivação existe. Vale reservar uma hora para três coisas.

Extrair o padrão. Origens, endpoints alvo, agentes de usuário e horários. Essa caracterização é o que permite escrever regras específicas em vez de genéricas — e regra específica não atrapalha usuário legítimo.

Ajustar os limites com o dado real. O ataque revelou tanto o tráfego anômalo quanto o normal. Números escolhidos com essa informação são muito melhores que os estimados antes.

Anotar o que funcionou. Qual medida derrubou o volume, quanto tempo levou para aplicar, o que atrapalhou. Na próxima vez, a diferença entre quinze minutos e duas horas está nesse registro.

Vale registrar também o que não era ataque. Boa parte dos picos investigados acaba sendo integração de cliente mal escrita ou robô de indexação agressivo — e o tratamento nesses casos é conversa, não bloqueio. Confundir os dois custa relacionamento.

Suba um servidor em minutos

Preço em real, suporte em português e dados no Brasil.

Criar conta