Rate Limiting nas APIs do servidor de MU Online: como evitar abuso e ataques
Implemente rate limiting nas APIs do painel e do site do seu servidor de MU Online, protegendo login, ranking e loja contra brute-force, scraping abusivo e sobrecarga de banco de dados.
Toda API exposta pelo site ou painel de um servidor de MU Online — login, consulta de ranking, resgate de código promocional, compra na loja de WCoin — é um alvo em potencial para abuso: bots tentando adivinhar senhas, scrapers batendo no ranking centenas de vezes por segundo, ou simplesmente um pic
Toda API exposta pelo site ou painel de um servidor de MU Online — login, consulta de ranking, resgate de código promocional, compra na loja de WCoin — é um alvo em potencial para abuso: bots tentando adivinhar senhas, scrapers batendo no ranking centenas de vezes por segundo, ou simplesmente um pico de tráfego legítimo que derruba o banco de dados. Rate limiting é a técnica de limitar quantas requisições uma origem pode fazer em uma janela de tempo, e é uma das defesas de infraestrutura mais custo-benefício que existem: poucas linhas de configuração evitam horas de instabilidade e uma boa fração de tentativas de invasão. Este tutorial cobre a implementação em duas camadas — proxy reverso (Nginx) e aplicação (PHP/Node com Redis) — aplicada aos endpoints mais sensíveis de um servidor de MU.
Por que APIs de servidor de MU são alvo constante
Diferente de um site institucional comum, o site de um servidor de MU tem incentivo econômico direto por trás de vários endpoints: ranking influencia reputação e atrai jogadores, login guarda contas com itens valiosos, e a loja movimenta dinheiro real via WCoin. Isso atrai três tipos de abuso recorrentes: brute-force de login (testar senhas em massa), scraping de ranking (bots capturando dados para sites de terceiros ou sobrecarregando o banco), e abuso de resgate de código (scripts tentando resgatar cupons promocionais em volume antes que expirem).
Camadas onde aplicar rate limiting
| Camada | O que controla | Granularidade |
|---|---|---|
| Proxy reverso (Nginx/Cloudflare) | Volume bruto de requisições por IP | Grosseira, mas muito barata em recursos |
| Aplicação (PHP/Node) | Regras de negócio: tentativas de login por conta, resgates por jogador | Fina, ciente do contexto |
| Banco de dados | Conexões simultâneas e queries lentas | Última linha de defesa, já em modo de emergência |
Nenhuma camada sozinha é suficiente. O proxy pega o volume bruto de bots simples; a aplicação pega abuso mais sofisticado que distribui requisições entre poucos IPs, mas concentra em uma conta.
Configurando rate limiting no Nginx
O módulo limit_req do Nginx é a forma mais eficiente de barrar volume antes que ele chegue ao PHP-FPM ou Node.
# No bloco http {}
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=ranking:10m rate=30r/m;
limit_req_zone $binary_remote_addr zone=geral:10m rate=60r/m;
server {
location /painel/login.php {
limit_req zone=login burst=2 nodelay;
limit_req_status 429;
fastcgi_pass unix:/run/php/php-fpm.sock;
}
location /api/ranking {
limit_req zone=ranking burst=10 nodelay;
limit_req_status 429;
proxy_pass http://127.0.0.1:3000;
}
location / {
limit_req zone=geral burst=20 nodelay;
}
}
rate=5r/mlimita a 5 requisições por minuto por IP na zona de login.burst=2 nodelaypermite um pequeno estouro de tráfego sem enfileirar a requisição, comum em recarregamentos duplos de página.limit_req_status 429devolve o código HTTP correto ("Too Many Requests") em vez do padrão 503, facilitando o tratamento no front-end.
Rate limiting na aplicação com Redis
Para regras que dependem de contexto de negócio (conta específica, não apenas IP), implemente o controle na aplicação usando Redis como armazenamento compartilhado dos contadores.
<?php
function verificarRateLimit(Redis $redis, string $chave, int $limite, int $janelaSegundos): bool {
$atual = $redis->incr($chave);
if ($atual === 1) {
$redis->expire($chave, $janelaSegundos);
}
return $atual <= $limite;
}
// Uso no endpoint de login
$chave = 'login_tentativas:' . $_SERVER['REMOTE_ADDR'] . ':' . $_POST['usuario'];
if (!verificarRateLimit($redis, $chave, 5, 300)) {
http_response_code(429);
die(json_encode(['erro' => 'Muitas tentativas. Tente novamente em alguns minutos.']));
}
Essa abordagem combina IP e nome de usuário na chave, o que evita bloquear injustamente uma rede inteira (NAT de operadora móvel, por exemplo) enquanto ainda impede que um único bot tente senhas em massa contra uma conta específica.
Definindo limites por endpoint
Cada endpoint tem um perfil de uso legítimo diferente, e o limite deve refletir isso:
| Endpoint | Limite sugerido | Justificativa |
|---|---|---|
| Login do painel | 5 tentativas / 5 min por IP+conta | Uso legítimo raramente excede 2-3 tentativas |
| Resgate de código promocional | 10 tentativas / 10 min por conta | Evita brute-force de códigos curtos |
| Consulta de ranking (API pública) | 30 req/min por IP | Suporta atualização automática de página sem abrir brecha para scraping pesado |
| Compra na loja (WCoin) | 20 req/min por conta | Transações reais raramente excedem esse volume por usuário |
| Cadastro de nova conta | 3 cadastros / hora por IP | Limita criação em massa de contas para farm/bot |
Respondendo corretamente quando o limite é excedido
Devolver apenas um erro genérico frustra o jogador legítimo que só teve azar de atingir o teto. A resposta ideal inclui o cabeçalho Retry-After e uma mensagem clara:
<?php
http_response_code(429);
header('Retry-After: 60');
echo json_encode([
'erro' => 'rate_limit_excedido',
'mensagem' => 'Você atingiu o limite de tentativas. Tente novamente em 60 segundos.'
]);
No front-end, capture o status 429 e exiba uma contagem regressiva em vez de deixar o usuário martelar o botão de novo, o que só prolongaria o bloqueio.
Rate limiting específico para scraping de ranking
Sites de ranking de terceiros (agregadores de servidores de MU) costumam fazer scraping do seu ranking com alta frequência para manter seus próprios dados atualizados. Se isso sobrecarregar seu banco, considere: (1) publicar um endpoint de API oficial com cache de alguns minutos, servido por um job agendado em vez de consulta direta ao banco a cada requisição, e (2) aplicar um limite mais generoso, porém real, nesse endpoint público, deixando claro nos termos do site que scraping fora dessa API é proibido.
Monitoramento e ajuste fino
Rate limiting mal calibrado no primeiro dia é normal — o objetivo é registrar e ajustar. Mantenha um log das rejeições (429) com IP, endpoint e timestamp, e revise semanalmente nas primeiras semanas após o lançamento:
# Exemplo de contagem de rejeições por endpoint nos logs do Nginx
grep ' 429 ' /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn
Se um endpoint específico gera muitas rejeições de IPs distintos e sem padrão de abuso aparente, o limite provavelmente está calibrado abaixo do uso legítimo real — suba-o. Se a maioria das rejeições vem de poucos IPs repetidos, o limite está funcionando como esperado.
Rate limiting versus CAPTCHA e outras defesas complementares
Rate limiting não substitui CAPTCHA em formulários de cadastro e recuperação de senha — ele reduz o volume de tentativas, mas não distingue humano de bot dentro do limite permitido. Para endpoints de altíssimo risco (cadastro de conta, recuperação de senha), combine rate limiting com CAPTCHA (reCAPTCHA ou hCaptcha) e, quando fizer sentido, com verificação de e-mail antes de liberar totalmente a conta nova.
Erros comuns e soluções
| Sintoma | Causa provável | Solução |
|---|---|---|
| Jogadores legítimos bloqueados no login | Limite calibrado só por IP, sem considerar NAT compartilhado | Combine IP + conta na chave do rate limit |
| Rate limiting não reduz ataques de brute-force | Limite aplicado só na aplicação, sem proxy na frente | Adicione limit_req no Nginx como primeira camada |
| Página de ranking lenta mesmo com rate limiting | Consulta direta ao banco a cada requisição, sem cache | Sirva o ranking a partir de cache atualizado por job agendado |
| Erro genérico confunde o jogador | Resposta 429 sem Retry-After nem mensagem clara | Inclua cabeçalho Retry-After e mensagem específica no JSON de erro |
| Contadores de rate limit "somem" após reiniciar o servidor | Contadores em memória local em vez de Redis | Use armazenamento persistente/compartilhado (Redis) para os contadores |
Checklist de implementação de rate limiting
limit_reqconfigurado no Nginx para login, ranking e endpoints de loja.- Regras de negócio (IP + conta) implementadas na aplicação via Redis ou equivalente.
- Limites por endpoint definidos com base no volume legítimo esperado.
- Resposta 429 com
Retry-Aftere mensagem clara implementada. - CAPTCHA combinado em cadastro e recuperação de senha.
- Endpoint de ranking com cache agendado, reduzindo carga no banco.
- Logs de rejeição monitorados e limites ajustados nas primeiras semanas.
Com o rate limiting no ar, o próximo passo natural é revisar as demais camadas de segurança das APIs do painel — validação de entrada, proteção CSRF e autenticação — para que o servidor tenha uma defesa em profundidade real. Se a infraestrutura ainda está em fase de planejamento, vale revisar o guia de criação de servidor de MU Online para alinhar essas práticas desde a fundação. </content>
Perguntas frequentes
Rate limiting é a mesma coisa que firewall?
Não. Um firewall bloqueia com base em regras de rede (IP, porta, protocolo); rate limiting controla a frequência de requisições legítimas a um endpoint específico, independente da origem ser 'confiável' ou não. Os dois se complementam.
Onde aplico rate limiting: no Nginx, na aplicação ou nos dois?
O ideal são os dois. O Nginx (ou proxy reverso equivalente) filtra o volume bruto antes de chegar à aplicação, economizando recursos; a aplicação aplica regras mais finas, como limite por conta de usuário em vez de só por IP, o que o proxy sozinho não enxerga.
Rate limiting pode bloquear jogadores legítimos?
Pode, se configurado de forma muito agressiva ou sem considerar IPs compartilhados (redes móveis, NAT de provedor). Por isso é importante limitar por combinação de IP + conta quando possível, e sempre devolver uma mensagem clara em vez de simplesmente travar a página.
Preciso de Redis para implementar rate limiting?
Não é obrigatório, mas é a opção mais robusta quando você tem mais de um servidor web atrás de um balanceador, porque o contador de requisições precisa ser compartilhado entre instâncias. Para um único servidor, cache em arquivo ou em memória da aplicação já resolve a maioria dos casos.
Qual limite de requisições é razoável para a página de login?
Uma referência comum é 5 tentativas por IP a cada 5 minutos para login, com bloqueio progressivo (backoff) em tentativas subsequentes. Ajuste conforme o volume real de jogadores do seu servidor e monitore falsos positivos nas primeiras semanas.