Ir al contenido

FundamentosLección 10

Abuso de APIs y rate limiting

Fuerza bruta, credential stuffing, scraping y agotamiento de recursos. Cómo limitar el ritmo de las peticiones, qué algoritmo usar, dónde ponerlo y qué responder.

Intermedio15 min de lectura

Al terminar sabrás

  • Reconocer los ataques de abuso que la autenticación sola no frena.
  • Elegir entre ventana fija, ventana deslizante y token bucket.
  • Decidir la clave del límite (IP, usuario, API key) y dónde aplicarlo.
  • Responder con 429 y Retry-After, y defender el login sin bloquear a las víctimas.

Un endpoint perfectamente autenticado y autorizado puede seguir siendo un problema si alguien lo llama demasiadas veces: probar millones de contraseñas, descargar todo tu catálogo, enumerar usuarios o simplemente tumbar el servicio. La autenticación responde “¿quién eres?”; el rate limiting responde “¿cuántas veces?”.

AtaqueQué hacePor qué la autenticación no basta
Fuerza brutaPrueba muchas contraseñas contra una cuentaCada intento es una petición “válida”
Credential stuffingPrueba pares usuario/contraseña filtrados de otros sitiosMuchas cuentas, pocos intentos por cuenta
EnumeraciónDescubre usuarios, emails o ids válidosLas respuestas difieren por poco
ScrapingDescarga datos en masa con una cuenta legítimaEstá autenticado y autorizado
Agotamiento de recursosConsultas caras, ficheros grandes, páginas de 100 000 elementosLa petición es legal, el coste no
Abuso de lógicaCanjear un cupón mil veces, crear cuentas en masaCada paso individual está permitido
Botnetmiles de IPsPOST /loginUsuarios1ana@… / contraseña filtrada24013luis@… / contraseña filtrada4200: cuenta comprometidaDefensas: MFA, contraseñas filtradas, límites globales y por cuenta, detección de anomalías
Credential stuffing: pocos intentos por cuenta, desde muchas IPs. Un límite solo por IP o solo por cuenta no lo ve.
AlgoritmoCómo funcionaProsContras
Ventana fijaN peticiones por minuto natural (10:00–10:01)Simple, baratoPermite 2N en el cambio de ventana
Ventana deslizanteN peticiones en los últimos 60 sPrecisoMás memoria o aproximación
Token bucketBucket de capacidad C que se recarga a R tokens/sPermite ráfagas controladas; el más usadoDos parámetros que ajustar
Leaky bucketCola que se vacía a ritmo constanteSuaviza el tráficoAñade latencia
  1. Petición
  2. Recargartokens = min(C, tokens + Δt × R)
  3. ¿Queda al menos 1 token?sí → consumir 1 y continuarno → 429 + Retry-After
Token bucket: capacidad C (ráfaga máxima) y recarga R (ritmo sostenido).

Una implementación mínima, para entender el mecanismo (en producción usa una librería como Bucket4j, con el estado en Redis si tienes varias instancias):

TokenBucket.java
final class TokenBucket {
private final long capacity;
private final double refillPerNano;
private double tokens;
private long last = System.nanoTime();
TokenBucket(long capacity, Duration refillPeriod) {
this.capacity = capacity;
this.tokens = capacity;
this.refillPerNano = (double) capacity / refillPeriod.toNanos();
}
synchronized boolean tryConsume() {
long now = System.nanoTime();
tokens = Math.min(capacity, tokens + (now - last) * refillPerNano);
last = now;
if (tokens >= 1) {
tokens -= 1;
return true;
}
return false;
}
}
  • Por IP: útil contra un único origen, pero detrás de un NAT corporativo comparten IP cientos de usuarios, y una botnet tiene miles de IPs.
  • Por usuario o API key: lo más justo para tráfico autenticado.
  • Por cuenta objetivo en el login (intentos fallidos contra ana, vengan de donde vengan).
  • Global por endpoint: un techo de seguridad para operaciones caras.

Y en capas: CDN o WAF (volumen bruto), API gateway (por cliente y plan), aplicación (reglas que conocen el dominio, como intentos de login por cuenta).

Al rechazar, responde 429 Too Many Requests con Retry-After para que los clientes legítimos sepan cuándo reintentar:

HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/problem+json
{"title":"Too Many Requests","status":429,"detail":"Demasiados intentos. Vuelve a intentarlo en 30 segundos."}

Mini lab · 15 min

Diseña los límites

Objetivo: Elegir algoritmo, clave y parámetros para tres endpoints de una API de notas.

Tu API tiene:

  • POST /api/auth/login
  • GET /api/notes?page=…&size=…
  • POST /api/notes/{id}/export (genera un PDF, tarda ~2 s de CPU)
  1. Para cada endpoint, elige la clave del límite, el algoritmo y unos parámetros razonables.
  2. Con un token bucket de capacidad 10 y recarga de 1 token cada 6 s, ¿cuántas peticiones puede hacer un cliente en el primer minuto? ¿Y de forma sostenida por hora?
  3. ¿Qué otro límite, que no es de ritmo, necesita GET /api/notes?
Pista

En el punto 2, suma la ráfaga inicial y lo que se recarga durante el minuto.

Mostrar respuestaOcultar respuesta
  1. Una propuesta:
    • Login: doble límite. Por cuenta objetivo (p. ej., 5 fallos cada 15 min con retraso progresivo) y por IP (p. ej., token bucket de 20 con recarga de 1 cada 3 s). Además MFA y comprobación de contraseñas filtradas.
    • GET /api/notes: por usuario, token bucket generoso (p. ej., capacidad 60, recarga 1/s): es barato y lo usa la interfaz constantemente.
    • Export: por usuario y global, porque es caro. Por ejemplo, 5 por hora por usuario y un máximo de exportaciones simultáneas en todo el sistema (una cola).
  2. Primer minuto: 10 de ráfaga + 10 recargados (60 s / 6 s) = 20. Sostenido: 1 cada 6 s = 600 por hora.
  3. Un tamaño máximo de página (size ≤ 100, por ejemplo). Sin él, size=1000000 es un ataque de agotamiento de recursos en una sola petición.