Rate Limiting en las APIs del servidor de MU Online: cómo evitar abuso y ataques
Implementa rate limiting en las APIs del panel y del sitio de tu servidor de MU Online, protegiendo login, ranking y tienda contra fuerza bruta, scraping abusivo y sobrecarga de base de datos.
Toda API expuesta por el sitio o el panel de un servidor de MU Online — login, consulta de ranking, canje de código promocional, compra en la tienda de WCoin — es un blanco potencial de abuso: bots intentando adivinar contraseñas, scrapers golpeando el ranking cientos de veces por segundo, o simplem
Toda API expuesta por el sitio o el panel de un servidor de MU Online — login, consulta de ranking, canje de código promocional, compra en la tienda de WCoin — es un blanco potencial de abuso: bots intentando adivinar contraseñas, scrapers golpeando el ranking cientos de veces por segundo, o simplemente un pico de tráfico legítimo que tumba la base de datos. Rate limiting es la técnica de limitar cuántas solicitudes puede hacer un origen en una ventana de tiempo, y es una de las defensas de infraestructura más costo-efectivas que existen: pocas líneas de configuración evitan horas de inestabilidad y una buena fracción de intentos de intrusión. Este tutorial cubre la implementación en dos capas — proxy reverso (Nginx) y aplicación (PHP/Node con Redis) — aplicada a los endpoints más sensibles de un servidor de MU.
Por qué las APIs de servidores de MU son un blanco constante
A diferencia de un sitio institucional común, el sitio de un servidor de MU tiene un incentivo económico directo detrás de varios endpoints: el ranking influye en la reputación y atrae jugadores, el login guarda cuentas con ítems valiosos, y la tienda mueve dinero real vía WCoin. Esto atrae tres tipos de abuso recurrentes: fuerza bruta de login (probar contraseñas en masa), scraping de ranking (bots capturando datos para sitios de terceros o sobrecargando la base de datos), y abuso de canje de código (scripts intentando canjear cupones promocionales en volumen antes de que expiren).
Capas donde aplicar rate limiting
| Capa | Qué controla | Granularidad |
|---|---|---|
| Proxy reverso (Nginx/Cloudflare) | Volumen bruto de solicitudes por IP | Gruesa, pero muy barata en recursos |
| Aplicación (PHP/Node) | Reglas de negocio: intentos de login por cuenta, canjes por jugador | Fina, consciente del contexto |
| Base de datos | Conexiones simultáneas y queries lentas | Última línea de defensa, ya en modo de emergencia |
Ninguna capa sola es suficiente. El proxy detiene el volumen bruto de bots simples; la aplicación detiene abusos más sofisticados que distribuyen solicitudes entre pocas IPs, pero se concentran en una cuenta.
Configurando rate limiting en Nginx
El módulo limit_req de Nginx es la forma más eficiente de frenar el volumen antes de que llegue a PHP-FPM o Node.
# En el bloque 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 solicitudes por minuto por IP en la zona de login.burst=2 nodelaypermite un pequeño exceso de tráfico sin encolar la solicitud, algo común en recargas dobles de página.limit_req_status 429devuelve el código HTTP correcto ("Too Many Requests") en vez del 503 por defecto, facilitando el manejo en el front-end.
Rate limiting en la aplicación con Redis
Para reglas que dependen del contexto de negocio (cuenta específica, no solo IP), implementa el control en la aplicación usando Redis como almacenamiento compartido de los 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 en el 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' => 'Demasiados intentos. Vuelve a intentarlo en unos minutos.']));
}
Este enfoque combina IP y nombre de usuario en la clave, lo cual evita bloquear injustamente a toda una red (NAT de operador móvil, por ejemplo) mientras sigue impidiendo que un solo bot intente contraseñas en masa contra una cuenta específica.
Definiendo límites por endpoint
Cada endpoint tiene un perfil de uso legítimo diferente, y el límite debe reflejarlo:
| Endpoint | Límite sugerido | Justificación |
|---|---|---|
| Login del panel | 5 intentos / 5 min por IP+cuenta | El uso legítimo rara vez excede 2-3 intentos |
| Canje de código promocional | 10 intentos / 10 min por cuenta | Evita la fuerza bruta de códigos cortos |
| Consulta de ranking (API pública) | 30 req/min por IP | Soporta la actualización automática de la página sin abrir la puerta a scraping pesado |
| Compra en la tienda (WCoin) | 20 req/min por cuenta | Las transacciones reales rara vez exceden este volumen por usuario |
| Registro de nueva cuenta | 3 registros / hora por IP | Limita la creación masiva de cuentas para farm/bot |
Respondiendo correctamente cuando se excede el límite
Devolver solo un error genérico frustra al jugador legítimo que solo tuvo mala suerte al alcanzar el tope. La respuesta ideal incluye el encabezado Retry-After y un mensaje claro:
<?php
http_response_code(429);
header('Retry-After: 60');
echo json_encode([
'erro' => 'rate_limit_excedido',
'mensagem' => 'Alcanzaste el límite de intentos. Vuelve a intentarlo en 60 segundos.'
]);
En el front-end, captura el estado 429 y muestra una cuenta regresiva en lugar de dejar que el usuario siga presionando el botón, lo cual solo prolongaría el bloqueo.
Rate limiting específico para scraping de ranking
Los sitios de ranking de terceros (agregadores de servidores de MU) suelen hacer scraping de tu ranking con alta frecuencia para mantener sus propios datos actualizados. Si esto sobrecarga tu base de datos, considera: (1) publicar un endpoint de API oficial con caché de algunos minutos, servido por un job programado en vez de una consulta directa a la base de datos en cada solicitud, y (2) aplicar un límite más generoso, pero real, en ese endpoint público, dejando claro en los términos del sitio que el scraping fuera de esa API está prohibido.
Monitoreo y ajuste fino
Un rate limiting mal calibrado el primer día es normal — el objetivo es registrar y ajustar. Mantén un log de los rechazos (429) con IP, endpoint y timestamp, y revísalo semanalmente en las primeras semanas después del lanzamiento:
# Ejemplo de conteo de rechazos por endpoint en los logs de Nginx
grep ' 429 ' /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn
Si un endpoint específico genera muchos rechazos de IPs distintas y sin un patrón de abuso aparente, el límite probablemente está calibrado por debajo del uso legítimo real — súbelo. Si la mayoría de los rechazos provienen de pocas IPs repetidas, el límite está funcionando como se esperaba.
Rate limiting versus CAPTCHA y otras defensas complementarias
El rate limiting no reemplaza al CAPTCHA en formularios de registro y recuperación de contraseña — reduce el volumen de intentos, pero no distingue entre humano y bot dentro del límite permitido. Para endpoints de altísimo riesgo (registro de cuenta, recuperación de contraseña), combina rate limiting con CAPTCHA (reCAPTCHA o hCaptcha) y, cuando tenga sentido, con verificación de correo antes de habilitar totalmente la cuenta nueva.
Errores comunes y soluciones
| Síntoma | Causa probable | Solución |
|---|---|---|
| Jugadores legítimos bloqueados en el login | Límite calibrado solo por IP, sin considerar NAT compartido | Combina IP + cuenta en la clave del rate limit |
| El rate limiting no reduce los ataques de fuerza bruta | Límite aplicado solo en la aplicación, sin proxy al frente | Agrega limit_req en Nginx como primera capa |
| Página de ranking lenta incluso con rate limiting | Consulta directa a la base de datos en cada solicitud, sin caché | Sirve el ranking desde una caché actualizada por un job programado |
| Un error genérico confunde al jugador | Respuesta 429 sin Retry-After ni mensaje claro | Incluye el encabezado Retry-After y un mensaje específico en el JSON de error |
| Los contadores de rate limit "desaparecen" al reiniciar el servidor | Contadores en memoria local en vez de Redis | Usa almacenamiento persistente/compartido (Redis) para los contadores |
Lista de verificación de implementación de rate limiting
limit_reqconfigurado en Nginx para login, ranking y endpoints de tienda.- Reglas de negocio (IP + cuenta) implementadas en la aplicación vía Redis o equivalente.
- Límites por endpoint definidos según el volumen legítimo esperado.
- Respuesta 429 con
Retry-Aftery mensaje claro implementada. - CAPTCHA combinado en registro y recuperación de contraseña.
- Endpoint de ranking con caché programada, reduciendo la carga en la base de datos.
- Logs de rechazo monitoreados y límites ajustados en las primeras semanas.
Con el rate limiting en funcionamiento, el siguiente paso natural es revisar las demás capas de seguridad de las APIs del panel — validación de entrada, protección CSRF y autenticación — para que el servidor tenga una defensa en profundidad real. Si la infraestructura todavía está en fase de planificación, vale la pena revisar la guía de creación de servidor de MU Online para alinear estas prácticas desde la fundación. </content>
Preguntas frecuentes
¿Rate limiting es lo mismo que un firewall?
No. Un firewall bloquea con base en reglas de red (IP, puerto, protocolo); el rate limiting controla la frecuencia de solicitudes legítimas a un endpoint específico, sin importar si el origen es 'confiable' o no. Ambos se complementan.
¿Dónde aplico rate limiting: en Nginx, en la aplicación o en ambos?
Lo ideal es en ambos. Nginx (o un proxy reverso equivalente) filtra el volumen bruto antes de que llegue a la aplicación, ahorrando recursos; la aplicación aplica reglas más finas, como un límite por cuenta de usuario en vez de solo por IP, algo que el proxy por sí solo no puede ver.
¿El rate limiting puede bloquear a jugadores legítimos?
Puede, si se configura de forma muy agresiva o sin considerar IPs compartidas (redes móviles, NAT de proveedor). Por eso es importante limitar por combinación de IP + cuenta cuando sea posible, y siempre devolver un mensaje claro en vez de simplemente bloquear la página.
¿Necesito Redis para implementar rate limiting?
No es obligatorio, pero es la opción más robusta cuando tienes más de un servidor web detrás de un balanceador, porque el contador de solicitudes necesita compartirse entre instancias. Para un solo servidor, una caché en archivo o en memoria de la aplicación ya resuelve la mayoría de los casos.
¿Qué límite de solicitudes es razonable para la página de login?
Una referencia común es 5 intentos por IP cada 5 minutos para el login, con bloqueo progresivo (backoff) en intentos posteriores. Ajusta según el volumen real de jugadores de tu servidor y monitorea falsos positivos en las primeras semanas.