Express.js
Parámetros de ruta y ownership
Validación de parámetros, resolución de recursos, ownership, autorización y aislamiento multi-tenant.
- Última actualización
- Actualizada
- Nivel
- Fundamentos
Express.js
Validación de parámetros, resolución de recursos, ownership, autorización y aislamiento multi-tenant.
Express captura params como strings:
router.get('/:orderId', async (request, response) => {
const { orderId } = request.params;
});El matching solo confirma que el path encaja. No garantiza que orderId tenga el formato esperado, que exista una orden ni que el actor pueda verla.
segmento de URL
↓
validación de formato
↓
resolución del recurso
↓
autorización sobre ese recurso
↓
ejecuciónSaltar cualquiera de estas etapas produce errores de contrato o seguridad.
const value = Number(request.params.orderId);
if (!Number.isSafeInteger(value) || value <= 0) {
throw new InvalidRouteParameterError('orderId');
}parseInt('12abc') devuelve 12, por lo que puede aceptar entradas ambiguas. Conviene validar el string completo.
const orderId = z.string().uuid().parse(request.params.orderId);Un slug necesita reglas de longitud, caracteres y canonicalización. Un slug válido puede cambiar, colisionar o exponer información; no es equivalente a una clave interna.
/not-a-uuid
→ 400 o error de validación del contrato
/550e8400-e29b-41d4-a716-446655440000
→ formato válido, pero puede no existirCuando el formato es válido y el recurso no existe, normalmente corresponde un 404. No conviertas cualquier ausencia en 400.
Autenticar al usuario no basta:
const order = await orders.findById(orderId);
if (!order || order.customerId !== actor.id) {
throw new OrderNotFoundError();
}Responder 404 tanto para inexistencia como para recurso no visible puede reducir enumeración. Otra política puede usar 403 cuando revelar existencia es aceptable. Lo importante es definirla conscientemente.
Nunca confíes en el tenant recibido por path o body como fuente de autoridad:
/businesses/:businessId/orders/:orderIdDebes comprobar:
Una query tenant-aware reduce el riesgo:
SELECT *
FROM orders
WHERE id = $1
AND business_id = $2;En /businesses/:businessId/branches/:branchId, validar ambos UUIDs no demuestra que la branch pertenezca al business. La relación debe formar parte de la consulta o regla.
router.param('orderId', async (request, response, next, value) => {
const orderId = orderIdSchema.parse(value);
response.locals.orderId = orderId;
next();
});Puede centralizar conversión, pero cargar automáticamente entidades tiene trade-offs:
Úsalo para lógica verdaderamente común y pequeña.
El cliente codifica segmentos con encodeURIComponent. No concatenes valores sin escapar al generar links.
Evita IDs que admitan /, .. o caracteres de control. Para slugs, decide si ABC y abc son equivalentes y si debes redirigir a una versión canónica.
router.get('/:orderId', validateOrderId, async (request, response) => {
const actor = response.locals.actor;
const orderId = response.locals.orderId;
const order = await getOrder.execute({ actor, orderId });
response.status(200).json({ data: order });
});El middleware valida transporte. El caso de uso resuelve y autoriza porque necesita estado real del dominio.
Una autorización previa no garantiza que el recurso siga existiendo. La actualización debe verificar filas afectadas o utilizar transacción/locking cuando corresponda.
Son simples y eficientes, pero facilitan enumeración. Esto no sustituye autorización; usar UUID tampoco la sustituye.
Dos slugs que se normalizan igual necesitan una constraint en base de datos, no solo validación en Express.
Puede provocar trabajo innecesario o logs enormes. Limita longitud antes de validaciones costosas.
Number y no verificar NaN o rango.tenantId del path directamente en una query privilegiada.router.param para todas las rutas.Incluye:
IDs públicos opacos reducen enumeración y desacoplan detalles internos, pero aumentan tamaño y complejidad. IDs numéricos son válidos si la autorización es correcta. La seguridad no debe depender de que el identificador sea difícil de adivinar.
businessId del path?router.param?Query parameters, filtros y límites trata el pequeño lenguaje público utilizado para consultar colecciones.