Hasta Skynet lo usaría: rate-limit-flexible protege tus endpoints serverless de ataques DDoS
Capítulo 1: Café Frío y el Despertar del Apocalipsis API
Imaginémonos la siguiente historia: Estás tranquilamente en tu escritorio desayunando un clásico pan con queso derretido y una taza de café bien caliente (el duo sagrado del consultor) mientras planeas tu día con toda la calma del mundo. Después de todo, ¿siempre hay paz y tranquilidad en el área de Operaciones, cierto?
Cuando de repente, comienzas a ser el centro de atención no deseado con una serie de correos y mensajes de un cliente diciendo que el proyecto en el que estás trabajando está respondiendo mucho más lento de lo normal. Pese a los mensajes, no le prestas mucha importancia y asumes que se debe a que están utilizando QA, por lo que es normal un poco más de lentitud de lo esperado.
Pero cuando comienzas a trabajar y abres el dashboard de Serverless ves que los endpoints que estás desarrollando tiene una cantidad inhumana de peticiones nunca antes vista por un mortal y hay varios colegas de otras áreas preguntando por qué hay tantos workers encolados. Bueno, ahora supongamos que esa persona encargada de ese endpoint soy yo, ese cliente era el cliente de un proyecto muy importante, esas peticiones llegaban a 15.000 en un periodo de 3 horas y ese café caliente junto con ese legendario pan con queso ya estaban fríos.
Claro, se me había pasado completamente aplicar un rate limit a los endpoints de serverless mientras el cliente aplicaba un Ethical Hacking, quedando yo como todo un campeón.
Moraleja: hasta Skynet, en su afán por conquistar el mundo, pondría límites a sus APIs. Porque incluso las máquinas saben que, a veces, los humanos (y sus pruebas de estrés) son el verdadero DDoS.
Capítulo 2: La Llegada de Skynet, cuando el DDoS Toca a tu Puerta
Imagina que hay 15.000 máquinas llamado a la puerta de tu API al mismo tiempo, como si fueran ejércitos de Skynet probando tus defensas. ¿Y tu protección? Nada. Tu endpoint está tan desprotegido como John Connor en su primer encuentro con un Terminator.
Por suerte, existe un "Terminator amigo": los middlewares del rate limit. Son como ese T-800 reprogramado que protege al héroe: no elimina las peticiones, pero las frena antes de que te maten el servidor.
"¿Un momento, esto no es lo mismo de lo que hablaste en tu artículo de Zod y los middlewares?". Te acercaste bastante. Aquí el middleware no valida datos… bloquea solicitudes como si fuera Arnold Schwarzenegger diciendo "Hasta la vista, baby" a las peticiones malintencionadas. Te dejo el artículo por si quieres profundizar más en el tema de los middlewares.

Capítulo 3: Seguridad Nivel Cyberdyne, primeros Pasos con Rate Limit
Ahora bien, lo que nos compete. Miremos este endpoint actual, se ve tan inocente como Jhon Connor antes del Juicio Final:
const handlerCreateUser: Handler = async event => {
try {
const createUser = event.body as User;
const user = await userService.create(createUser);
return formatJSONResponse(201, user);
} catch (error) {
// ...
}
};
export const handler = middy(handlerCreateUser).use(validateBody());Como podrán ver, la seguridad que presenta es bastante baja, equivalente a la de Cyberdyne Systems en los 80'. Por lo menos se está usando un middleware para validar el body (lo cuál es un primer paso). Pese a esto, cualquiera con ánimo de pasarse de listo podría enviarnos un T-800 al pasado para realizar múltiples solicitudes de red sin parar (o simplemente un ciclo for con múltiples llamadas de red al endpoint).
La solución a esto es bastante simple. Instalar un escudo "anti-DDoS".
$ npm i rate-limiter-flexibleCapítulo 4: Reprogramando al T-800, implementando Rate Limiting
Una vez instalado, nos toca "reprogramar" a nuestro héroe: El T-800 rate limit que nos defenderá de los ataques. Para esto, solo basta con configurar su nuevo chip de control (aka. crear un nuevo archivo con nuestra configuración). En este caso, vamos a limitar nuestro endpoint para que tengo un máximo de diez peticiones por cada minuto.
import { RateLimiterMemory } from 'rate-limiter-flexible';
export const rateLimiter = new RateLimiterMemory({
points: 10 // 10 peticiones
duration: 60 // por cada minuto
});Ahora solo nos queda hacer el upgrade a nuestro T-800, añadiéndolo en el handler:
import { rateLimiter } from "./config/rate-limiter.config.ts";
import { RateLimiterRes } from "rate-limiter-flexible";
const handlerCreateUser: Handler = async event => {
try {
// Añadimos la validación de rate limit
const clientIp = event.requestContext?.identity?.sourceIp || "unknown";
await rateLimiter.consume(clientIp);
const createUser = event.body as User;
const user = await userService.create(createUser);
return formatJSONResponse(201, user);
} catch (error) {
if (error instanceof RateLimiterRes)
return formatJSONResponse(429, {
message: `Too Many Request. I'll be back... after 60 seconds`
});
// Resto de validaciones...
}
};
export const handler = middy(handlerCreateUser).use(validateBody);
Capítulo 5: El T-800 SRP Edition, Rate Limit como Middleware
Ya estamos bastante protegidos frente a cualquier ataque. Pero como he dicho en artículos previos, aquí no tomamos en serio el Principio de Responsabilidad Única (SRP), es por esto que toca actualizar nuevamente nuestro T-800 SRP Edition.
Hacer esto es simple, solo creamos un nuevo archivo que contenga nuestro middleware encargado de validar el rate limit.
import { rateLimiter } from "./config/rate-limiter.config.ts";
export const rateLimit = () => ({
before: async request => {
const clientIp = request.requestContext?.identity?.sourceIp || "unknown";
try {
await rateLimiter.consume(clientIp);
}
catch(error){
return formatJSONResponse(429, {
message: `Too Many Request. I'll be back... after 60 seconds`
});
}
}
})Finalmente, solo nos queda importar este middleware en el handler:
import { rateLimit } from "./middlewares/rate-limit.middleware.ts";
const handlerCreateUser: Handler = async event => {
try {
const createUser = event.body as User;
const user = await userService.create(createUser);
return formatJSONResponse(201, user);
} catch (error) {
// Validaciones...
}
};
export const handler = middy(handlerCreateUser)
.use(validateBody())
.use(rateLimit());Capítulo 6: De T-800 a Skynet. Rate Limit en Producción y Reflexiones Finales
Este rate limit en memoria es tu T-800 modelo 101 (el más básico), pero en producción es posible que necesites evolucionar a modelos más avanzados. Según la escala de tu Skynet particular, podrías necesitar combinarlo con bases de datos como Redis o PostgreSQL, entre otros tantos.
En un mundo donde Skynet controla los bots scrappers y los hackers parecen ejércitos de T-1000, nuestras APIs no tienen excusa para andar desprotegidas. Recuerda: un buen rate limit es como tener tu propio T-800 reprogramado. Quizá no elimina las amenazas, pero sí las frenará con la eficacia de Arnold en su mejor momento.
Así que la próxima vez que despliegues un endpoint serverless, pregúntate: ¿Esto aguantaría un ataque coordinado de máquinas rebeldes? Si la respuesta hace que tu café mañanero se enfríe más rápido de lo normal, es señal inequívoca: necesitas tu escudo anti-DDoS.
Hasta la próxima, y recuerda: "I'll be back..." con más estrategias para mantener tus APIs a salvo (y tu café caliente).
Referencias:




Member discussion