Hoy · Hoja de ruta
- 1. Aprende: Las protecciones que no pueden faltar en un servicio expuesto a internet.
- 2. Haz: Aplícalas a tu API y comprueba que funcionan.
- 3. Comprueba: Intentas romper tu propio servicio y no lo consigues.
Antes de empezar · 5 minutos, sin apuntes
- ¿Qué podría hacer alguien con acceso a tu API tal como está hoy?
- ¿Qué pasa si envía un cuerpo de cien megabytes?
- ¿Y si llama mil veces por segundo?
La lista mínima
| Riesgo | Protección |
|---|---|
| Datos inválidos o inesperados | Validación con lista blanca en el servidor |
| Inyección de HTML | Escapar todo dato al generar la página |
| Salirse de la carpeta pública | Comprobar la ruta resuelta |
| Cuerpos enormes | Límite de tamaño en el analizador |
| Abuso de llamadas | Limitación por dirección y ventana de tiempo |
| Cabeceras que delatan | Quitar la que anuncia el framework |
| Escritura sin permiso | Autorización en las rutas que modifican |
| Secretos filtrados | Todo en el entorno, nada en el repositorio |
Aplicarlas
app.disable("x-powered-by");
app.use(express.json({ limit: "100kb" }));
const llamadas = new Map();
export function limitar(peticion, respuesta, next) {
const clave = peticion.ip;
const ahora = Date.now();
const registro = llamadas.get(clave) ?? { desde: ahora, total: 0 };
if (ahora - registro.desde > 60_000) {
registro.desde = ahora;
registro.total = 0;
}
registro.total += 1;
llamadas.set(clave, registro);
if (registro.total > 100) {
return respuesta.status(429).json({ error: "Demasiadas peticiones", codigo: "LIMITE" });
}
next();
}
Es un limitador de andar por casa —vive en memoria y se pierde al reiniciar— pero enseña la idea, y el 429 es el código que corresponde.
Autorización para lo que modifica
export function requiereClave(peticion, respuesta, next) {
const enviada = peticion.headers["x-api-key"];
if (enviada !== configuracion.claveApi) {
throw new ErrorNoAutenticado("Clave de API no válida");
}
next();
}
router.post("/", requiereClave, crearProducto);
router.delete("/:id", requiereClave, borrarProducto);
Leer es público; escribir, no
Una clave en una cabecera es la forma más simple de proteger las operaciones que modifican, y es suficiente para un proyecto de aula. No es un sistema de usuarios: no distingue quién llama, no caduca y quien la tenga puede todo.
Lo que sí enseña es dónde se pone la comprobación —en el servidor, antes del manejador— y la diferencia entre 401 y 403: la primera es «no sé quién eres», la segunda «sé quién eres y no puedes».
Una regla no admite excepción: si algún día almacenas contraseñas, se guardan cifradas con una función pensada para eso, nunca en claro ni con un resumen sin sal. En este módulo directamente no guardamos ninguna.
Tarea 14 · Blindar
- Aplica el límite de tamaño y quita la cabecera del framework.
- Escribe el limitador de peticiones y devuelve 429.
- Protege con clave las rutas que modifican.
- Repasa que toda entrada sigue validándose con lista blanca.
- Intenta atacar tu propio servicio de cinco formas distintas y anota el resultado.
- Ejecuta
npm audity resuelve lo que aparezca.
Checkpoint · fin de la sesión 14
- Las operaciones que modifican exigen autorización.
- Hay límites de tamaño y de frecuencia.
- Ningún secreto está en el repositorio.
- Has intentado romper tu propio servicio.
Ver respuestas
1 · Un 429.
2 · 401 es no autenticado; 403, autenticado pero sin permiso.
3 · En el servidor, antes del manejador de la ruta.