Hardening do Nginx no HestiaCP: como mitigar ataques DDoS de Camada 7 e otimizar a cache de conteúdo dinâmico

Como personalizar os templates Nginx do HestiaCP para limitar pedidos, bloquear ligações lentas e ativar a FastCGI Cache, reduzindo o impacto dos ataques DDoS de aplicação e acelerando as páginas dinâmicas.

Hardening do Nginx no HestiaCP: como mitigar ataques DDoS de Camada 7 e otimizar a cache de conteúdo dinâmico

Quem administra um servidor com HestiaCP mais tarde ou mais cedo encontra um problema conhecido: o site responde lentamente, a CPU dispara e os registos do Nginx enchem-se de milhares de pedidos que, isoladamente, parecem inofensivos. Muitas vezes não se trata de um pico de tráfego "bom", mas sim de um ataque DDoS de aplicação: um tipo de ataque que não procura saturar a largura de banda da rede, mas sim esgotar os recursos do servidor (CPU, memória, ligações à base de dados) enviando pedidos HTTP que parecem perfeitamente legítimos. Neste guia vamos intervir diretamente nos templates Nginx do HestiaCP para adicionar proteções contra este tipo de ataque e, ao mesmo tempo, aproveitar a cache do Nginx para aliviar a carga sobre as páginas dinâmicas (por exemplo, geradas por PHP), tornando o site mais resistente mesmo quando o tráfego é legítimo mas muito elevado.

O que é um ataque DDoS de "Camada 7" e porque envolve o Nginx

Os ataques DDoS (Distributed Denial of Service, negação de serviço distribuída) dividem-se em várias categorias de acordo com a camada do modelo OSI que visam. Os ataques às camadas 3 e 4 (rede e transporte) procuram saturar a largura de banda ou as tabelas de ligação, e são normalmente filtrados antes de chegarem ao servidor, por exemplo pelo fornecedor de hosting ou por um serviço de proteção como uma CDN. Os ataques de Camada 7 (a camada de "aplicação", onde vive o protocolo HTTP) são mais insidiosos porque cada pedido individual é quase indistinguível de um pedido real de um utilizador: um pedido a uma página de login, a uma pesquisa no site ou a um endpoint que consulta a base de dados, repetido milhares de vezes por segundo a partir de muitos endereços IP diferentes, pode derrubar um servidor mesmo com uma ligação de rede longe de estar saturada.

Como o Nginx é o servidor web que recebe primeiro cada pedido HTTP numa instalação HestiaCP (quer funcione sozinho, quer como proxy inverso diante do Apache), é o local ideal para aplicar as primeiras contramedidas: pode limitar quantos pedidos aceita de um único endereço IP, quantas ligações simultâneas mantém abertas, e pode "absorver" parte do tráfego servindo respostas a partir da cache em vez de as gerar novamente de cada vez.

Porque não se devem modificar os templates originais do HestiaCP

No HestiaCP, a configuração Nginx de cada domínio não é escrita à mão: é gerada automaticamente a partir de ficheiros modelo chamados templates, que contêm marcadores como %domain%, %user%, %docroot% ou %backend_lsnr%, substituídos pelos valores reais do site quando a configuração é criada ou reconstruída. Os templates encontram-se em dois locais no servidor, dependendo do cenário:

  • Nginx como único servidor web (com PHP-FPM): /usr/local/hestia/data/templates/web/nginx/php-fpm/
  • Nginx como proxy inverso diante do Apache: /usr/local/hestia/data/templates/web/nginx/

A documentação oficial do HestiaCP recomenda explicitamente não modificar os ficheiros de template predefinidos (por exemplo default.tpl): sempre que o painel é atualizado, estes ficheiros são substituídos e as alterações seriam perdidas. O procedimento correto é copiar o template que se quer personalizar com um novo nome, e modificar apenas as cópias. Cada template normalmente precisa de vários ficheiros com o mesmo nome base mas extensões diferentes, por exemplo hardened.tpl, hardened.stpl (a variante com certificado SSL) e, se existir, o script de suporte hardened.sh.

Criar o template personalizado passo a passo

Depois de se ligar ao servidor via SSH como root (ou com um utilizador com as permissões adequadas), criar um template baseado no predefinido faz-se assim:

cd /usr/local/hestia/data/templates/web/nginx/php-fpm/
cp default.tpl hardened.tpl
cp default.stpl hardened.stpl

A partir daqui, hardened.tpl e hardened.stpl são duas cópias independentes que podem ser modificadas sem risco de perder as alterações na próxima atualização do HestiaCP. As secções seguintes mostram o que adicionar dentro destes ficheiros para obter as duas proteções principais: limitação de pedidos e cache.

Limitar os pedidos por endereço IP: limit_req

A primeira e mais eficaz contramedida contra um ataque de aplicação é impedir que um único endereço IP envie um número excessivo de pedidos em pouco tempo. O Nginx oferece para isso o módulo limit_req, que funciona em dois passos: primeiro define-se uma "zona de memória partilhada" que regista os pedidos por IP, depois aplica-se essa zona a um ou mais blocos location da configuração.

A definição da zona vai no bloco http global do Nginx (ficheiro /etc/nginx/nginx.conf, ou um ficheiro dentro de /etc/nginx/conf.d/ incluído automaticamente), não no template de cada domínio:

# /etc/nginx/conf.d/ratelimit.conf
limit_req_zone $binary_remote_addr zone=generic:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m;

Aqui, generic é uma zona que permite uma média de 30 pedidos por minuto por IP em todo o site, enquanto login é uma zona mais restritiva (10 pedidos por minuto) pensada especificamente para as páginas de autenticação, o alvo favorito habitual deste tipo de ataque. O valor 10m indica quanta memória é reservada para registar endereços IP: 10 MB são suficientes para cerca de 160.000 endereços distintos, mais do que suficiente para a maioria dos sites.

Feito isto, dentro do template hardened.tpl aplica-se a zona genérica a todo o site e a mais restritiva à página de login:

server {
    # ... diretivas geradas automaticamente pelo HestiaCP ...

    limit_req zone=generic burst=15 nodelay;

    location = /wp-login.php {
        limit_req zone=login burst=5 nodelay;
        include /etc/nginx/fastcgi_params;
        fastcgi_pass %backend_lsnr%;
    }
}

O parâmetro burst permite absorver pequenas rajadas de pedidos legítimos (por exemplo, quando um browser carrega vários recursos em paralelo) sem bloquear o visitante, enquanto nodelay evita que os pedidos em excesso sejam colocados em fila, devolvendo imediatamente um erro 503 Service Temporarily Unavailable a quem exceder o limite. Assim, um bot que martela a página de login começa rapidamente a receber erros e deixa de consumir recursos do servidor, enquanto um visitante normal não nota nada.

Limitar as ligações simultâneas: limit_conn

Outra técnica complementar consiste em limitar quantas ligações abertas ao mesmo tempo um único IP pode ter, útil contra ataques que abrem muitas ligações e as mantêm ocupadas o máximo de tempo possível. Também aqui a lógica é em dois passos:

# /etc/nginx/conf.d/ratelimit.conf
limit_conn_zone $binary_remote_addr zone=perip:10m;
server {
    limit_conn perip 20;
}

Com esta configuração, um único endereço IP não poderá ter mais de 20 ligações abertas simultaneamente a esse site: acima deste limite, o Nginx responde com um erro sem nem sequer passar o pedido ao backend PHP ou ao Apache, poupando recursos valiosos.

Proteger-se das ligações lentas (Slowloris)

Uma família de ataques de aplicação, conhecida como Slowloris, não envia muitos pedidos, mas abre ligações e mantém-nas ocupadas enviando dados muito lentamente, gota a gota, de forma a esgotar o número máximo de ligações que o servidor pode gerir. A contramedida é definir tempos limite rigorosos para a receção dos cabeçalhos e do corpo do pedido, para que o Nginx fecha as ligações demasiado lentas:

server {
    client_header_timeout 10s;
    client_body_timeout 10s;
    send_timeout 10s;
}

Os valores predefinidos do Nginx para estes tempos limite são de 60 segundos, tempo mais do que suficiente para um ataque Slowloris saturar as ligações disponíveis; reduzi-los para 10 segundos (um valor que pode ser ajustado de acordo com a qualidade média das ligações dos visitantes reais do site) reduz drasticamente a janela de ataque sem impacto percetível para utilizadores com uma ligação normal.

Bloquear IPs e User-Agents suspeitos

Quando, ao analisar os registos durante um ataque, se identificam endereços IP recorrentes ou um User-Agent anómalo usado pelo tráfego malicioso (muitos bots simples usam um User-Agent vazio ou claramente falso), pode adicionar-se um bloqueio direto no template:

server {
    # Bloquear um intervalo de IPs
    deny 203.0.113.0/24;

    # Bloquear pedidos com User-Agent vazio ou suspeito
    if ($http_user_agent ~* "^$|curl|wget|python-requests") {
        return 403;
    }
}

Esta técnica deve ser usada com cautela: bloquear por User-Agent só é útil contra os bots mais rudimentares, porque os mais sofisticados imitam perfeitamente o User-Agent de um browser real, mas continua a ser um filtro de custo quase nulo que pode eliminar parte do tráfego indesejado antes de chegar ao resto da configuração.

Otimizar a cache de conteúdo dinâmico com FastCGI Cache

As proteções vistas até agora reduzem o tráfego malicioso, mas o segundo pilar da resiliência consiste em garantir que o tráfego legítimo (mesmo que seja muito elevado, por exemplo durante um pico de visitas orgânico) pese o menos possível sobre o servidor. Para sites baseados em PHP (incluindo WordPress) geridos pelo HestiaCP com Nginx e PHP-FPM, a solução é a FastCGI Cache integrada no Nginx: na primeira vez que uma página é pedida, o Nginx gera-a normalmente passando o pedido ao PHP-FPM, mas guarda uma cópia; os pedidos seguintes à mesma página são servidos diretamente a partir da cache, sem voltar a envolver o PHP nem a base de dados, com um ganho de velocidade enorme e uma carga quase nula sobre o servidor.

Também aqui a zona de cache deve ser definida a nível global:

# /etc/nginx/conf.d/fastcgi-cache.conf
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=WPCACHE:100m inactive=60m max_size=1g;
fastcgi_cache_key "$scheme$request_method$host$request_uri";

Depois, dentro do template hardened.tpl, o HestiaCP prevê um mecanismo específico: basta adicionar, logo após o include fastcgi_params no bloco que trata os ficheiros PHP, uma referência ao ficheiro de configuração por domínio que o painel gera automaticamente quando a cache é ativada a partir da interface:

location ~ [^/]\.php(/|$) {
    fastcgi_pass %backend_lsnr%;
    fastcgi_index index.php;
    include /etc/nginx/fastcgi_params;
    include %home%/%user%/conf/web/%domain%/nginx.fastcgi_cache.conf*;

    fastcgi_cache WPCACHE;
    fastcgi_cache_valid 200 60m;
    fastcgi_cache_use_stale updating error timeout invalid_header http_500;
    add_header X-FastCGI-Cache $upstream_cache_status;
}

O cabeçalho X-FastCGI-Cache é muito útil para verificar o funcionamento: aparece na resposta HTTP com o valor HIT quando a página é servida a partir da cache, MISS quando é gerada pelo PHP e guardada em cache por pela primeira vez, e pode ser verificado facilmente no browser (ferramentas de desenvolvedor, separador Rede) ou no terminal com curl -I https://oseusite.com/. A opção fastcgi_cache_use_stale com o parâmetro updating é especialmente importante durante um pico de tráfego: se a cache de uma página expira exatamente no momento em que chegam centenas de pedidos simultâneos, o Nginx envia apenas um deles ao PHP para a regenerar e continua a servir a todos os outros a versão anterior (ligeiramente expirada) até a nova estar pronta, evitando que todos os pedidos atinjam o backend ao mesmo tempo.

Nem todas as páginas devem ser colocadas em cache: a área de administração de um CMS, ou páginas que mostram conteúdo personalizado para o utilizador autenticado, devem ser excluídas, caso contrário um utilizador poderia ver os dados de outro. O HestiaCP já trata parcialmente destes casos através do ficheiro nginx.fastcgi_cache.conf gerado pelo painel, mas convém verificar, para CMS específicos como o WordPress, se cookies como os de sessão ou de carrinho de compras desativam corretamente a cache para esse pedido através de uma diretiva fastcgi_cache_bypass baseada no valor do cookie.

Aplicar o novo template e verificar o resultado

Depois de prontos os ficheiros hardened.tpl e hardened.stpl, a ativação num domínio é totalmente gerida pela interface do HestiaCP:

  1. Aceder ao painel, abrir Web > [nome do domínio] > Edit.
  2. No menu suspenso de template Nginx, selecionar o novo template hardened (aparecerá automaticamente na lista depois de os ficheiros serem copiados).
  3. Guardar: o HestiaCP regenera a configuração do domínio a partir do novo template.

Em alternativa, a partir da linha de comandos, o mesmo resultado obtém-se com:

v-change-web-domain-tpl utilizador dominio.pt hardened
v-rebuild-web-domain utilizador dominio.pt yes

onde o último parâmetro yes força também o reinício dos serviços web envolvidos. Antes de aplicar a alteração em produção, é sempre recomendável verificar a sintaxe da configuração com nginx -t, que assinala eventuais erros sem interromper o serviço em curso, e testar o comportamento da cache e dos limites de pedidos num domínio de teste ou num horário de baixo tráfego.

Para limpar a cache FastCGI depois de uma alteração ao site (por exemplo uma atualização de tema ou de conteúdos), o HestiaCP oferece o botão "Purge Nginx Cache" na página de edição do domínio, ou o comando v-purge-nginx-cache utilizador dominio.pt na linha de comandos.

Conclusões

Nenhuma das medidas descritas neste guia é, por si só, uma proteção completa contra um ataque DDoS de aplicação sério e prolongado: para isso são necessárias soluções dedicadas antes do servidor, como uma CDN com proteção anti-DDoS ou uma Web Application Firewall. No entanto, combinar a limitação de pedidos, a limitação de ligações, tempos limite rigorosos para clientes lentos e uma cache FastCGI bem configurada nos templates Nginx do HestiaCP eleva significativamente o limiar de tráfego malicioso que o servidor é capaz de absorver por si só, melhorando ao mesmo tempo o desempenho do site nos momentos de tráfego legítimo elevado. É o tipo de intervenção de "baixo custo, alto benefício" que vale a pena aplicar em qualquer servidor HestiaCP exposto publicamente na internet.

Fontes oficiais

Partilhar

Qual é a sua reação?

Gosto Gosto 0
Não gosto Não gosto 0
Adoro Adoro 0
Divertido Divertido 0
Irritado Irritado 0
Triste Triste 0
Uau Uau 0