Reglas de Seguridad
Reglas de seguridad básicas para servicios HTTP de Node.js que manejan entradas no confiables, secretos y solicitudes salientes.
Receta
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
import { z } from "zod";
const CreateUserSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(120),
});
app.post("/users", (req, res) => {
const body = CreateUserSchema.safeParse(req.body);
if (!body.success) return res.status(400).json({ error: body.error.flatten() });
// ...
});Cuándo usarlo:
- Cada ruta que acepta JSON, parámetros de consulta o encabezados.
- Servicios que obtienen URLs proporcionadas por el usuario.
- Cualquier código que toque claves API o credenciales de base de datos.
Ejemplo de Funcionamiento
// src/config/env.ts
import { z } from "zod";
const EnvSchema = z.object({
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
PORT: z.coerce.number().default(3000),
});
export const env = EnvSchema.parse(process.env);// src/security/ssrf.ts
import { lookup } from "node:dns/promises";
const BLOCKED = [/^127\./, /^10\./, /^192\.168\./, /^169\.254\./, /^0\./];
export async function assertSafeUrl(raw: string) {
const url = new URL(raw);
if (!["http:", "https:"].includes(url.protocol)) throw new Error("protocol blocked");
const { address } = await lookup(url.hostname);
if (BLOCKED.some((re) => re.test(address))) throw new Error("ssrf blocked");
}// src/routes/preview.ts
app.post("/preview", async (req, res) => {
const { targetUrl } = z.object({ targetUrl: z.string().url() }).parse(req.body);
await assertSafeUrl(targetUrl);
const html = await fetch(targetUrl, { signal: AbortSignal.timeout(3_000) });
res.send(await html.text());
});Lo que esto demuestra:
- El entorno se valida al inicio: la falta de
JWT_SECRETfalla antes de escuchar. - La protección SSRF resuelve DNS y bloquea IPs privadas.
- Zod valida la forma de entrada antes de la obtención saliente.
Análisis Profundo
Cómo Funciona
- Valida en el límite de confianza (borde HTTP), no en lo profundo de los repositorios.
- Los secretos nunca se registran; se redactan en los serializadores del registrador estructurado.
- Usa helmet o plugins de framework para encabezados de seguridad.
- Limita la tasa de autenticación y los puntos finales costosos en el borde o middleware.
Tabla de Reglas de Seguridad
| Regla | Aplicación |
|---|---|
| Validar todas las entradas | Zod/esquema en la ruta |
| Secretos solo en env/vault | gitignore, escáneres de secretos |
| Protección SSRF en obtención saliente | DNS + lista negra de IP |
| Configuración explícita de Helmet/CORS | middleware |
npm audit alto+ bloqueado | CI |
Notas de TypeScript
z.infer<typeof Schema>tipifica los datos validados.- Evita
anyenreq.body: el tipo proviene solo del resultado del análisis.
Errores comunes
- Confiar en
req.bodydespués de una validación parcial - Campos adicionales pasados al ORM. Solución:.strict()en objetos Zod. - Registrar cuerpos de solicitud completos - PII en los registros. Solución: solo lista blanca de campos de registro.
- JWT en cadenas de consulta - Fugas a través de registros y Referer. Solución: solo encabezado de autorización.
- CORS predeterminado
*con credenciales - Violación de seguridad del navegador y fuga de datos. Solución: lista blanca de origen explícita. - Contaminación de prototipos a través de
Object.assign(req.body)- UsastructuredCloneu objeto de salida de análisis de esquema.
Alternativas
| Alternativa | Usar Cuándo | No Usar Cuándo |
|---|---|---|
| joi / valibot | Estándar del equipo | Ya estandarizado en Zod |
| WAF en el borde | DDoS, reglas OWASP | Reemplazar la validación de entrada |
| mTLS interno | Malla de servicios de confianza cero | Solo REST público |
Preguntas Frecuentes
¿Es suficiente el límite de express.json()?
Establece { limit: "100kb" } más validación de esquema; las cargas útiles grandes necesitan puntos finales de transmisión por separado.
¿Qué tan largo debe ser el secreto JWT?
Mínimo 32 bytes aleatorios; rota con ventana de verificación de doble clave.
¿Revinculación de DNS SSRF?
Resuelve y conecta a la IP en el mismo envoltorio de obtención protegido; tiempo de espera corto; bloquea las redirecciones a IPs privadas.
¿Validación de Fastify?
Usa @fastify/type-provider-typebox o el compilador Zod - valida antes de que se ejecute el manejador.
¿ValidationPipe de NestJS?
Habilítalo globalmente con whitelist: true y forbidNonWhitelisted: true.
¿Es suficiente el escaneo de dependencias?
No. Combina auditoría, Socket y reglas de codificación segura; los escáneres omiten fallas en la lógica de la aplicación.
¿Cómo manejar las cargas de archivos?
Escanea tipo/tamaño; almacena fuera de la raíz web; nunca ejecuta contenido cargado.
¿Secretos en Docker?
Inyecta en tiempo de ejecución a través de secretos del orquestador; no se hornean argumentos de construcción en las capas de la imagen.
¿Dónde limitar la tasa?
Pasarela API o @fastify/rate-limit / express-rate-limit en rutas de autenticación como mínimo.
¿Conjunto mínimo de encabezados de seguridad?
Content-Security-Policy (API JSON puede ser mínima), X-Content-Type-Options, Strict-Transport-Security detrás de TLS.
Relacionado
- Lista de Verificación de Reglas de Proyectos Node - reglas 6-10
- Reglas de Dependencia - cadena de suministro
- Reglas de API - límites de exposición de errores
Versiones de la pila: Esta página fue escrita para Node.js 24.18.0 (LTS Activo), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 y NestJS 11.