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.
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
429yRetry-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?”.
Qué ataques frena
Sección titulada «Qué ataques frena»| Ataque | Qué hace | Por qué la autenticación no basta |
|---|---|---|
| Fuerza bruta | Prueba muchas contraseñas contra una cuenta | Cada intento es una petición “válida” |
| Credential stuffing | Prueba pares usuario/contraseña filtrados de otros sitios | Muchas cuentas, pocos intentos por cuenta |
| Enumeración | Descubre usuarios, emails o ids válidos | Las respuestas difieren por poco |
| Scraping | Descarga datos en masa con una cuenta legítima | Está autenticado y autorizado |
| Agotamiento de recursos | Consultas caras, ficheros grandes, páginas de 100 000 elementos | La petición es legal, el coste no |
| Abuso de lógica | Canjear un cupón mil veces, crear cuentas en masa | Cada paso individual está permitido |
Algoritmos
Sección titulada «Algoritmos»| Algoritmo | Cómo funciona | Pros | Contras |
|---|---|---|---|
| Ventana fija | N peticiones por minuto natural (10:00–10:01) | Simple, barato | Permite 2N en el cambio de ventana |
| Ventana deslizante | N peticiones en los últimos 60 s | Preciso | Más memoria o aproximación |
| Token bucket | Bucket de capacidad C que se recarga a R tokens/s | Permite ráfagas controladas; el más usado | Dos parámetros que ajustar |
| Leaky bucket | Cola que se vacía a ritmo constante | Suaviza el tráfico | Añade latencia |
- Petición
- Recargartokens = min(C, tokens + Δt × R)
- ¿Queda al menos 1 token?sí → consumir 1 y continuarno →
429+Retry-After
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):
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; }}La clave del límite y dónde aplicarlo
Sección titulada «La clave del límite y dónde aplicarlo»- 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 RequestsRetry-After: 30Content-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/loginGET /api/notes?page=…&size=…POST /api/notes/{id}/export(genera un PDF, tarda ~2 s de CPU)
- Para cada endpoint, elige la clave del límite, el algoritmo y unos parámetros razonables.
- 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?
- ¿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
- 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).
- Primer minuto: 10 de ráfaga + 10 recargados (60 s / 6 s) = 20. Sostenido: 1 cada 6 s = 600 por hora.
- Un tamaño máximo de página (
size≤ 100, por ejemplo). Sin él,size=1000000es un ataque de agotamiento de recursos en una sola petición.