Control de acceso y autorización
El riesgo nº1 de OWASP. Cómo evitar IDOR/BOLA y aplicar deny-by-default.
El control de acceso roto es el riesgo nº1 del OWASP Top 10. Autenticar (saber quién eres) no es autorizar (saber qué puedes hacer). La mayoría de las brechas graves son fallos de autorización, no de login.
1. Deny-by-default
Toda ruta y recurso deben estar denegados salvo que se conceda acceso explícito. Si olvidas proteger un endpoint, debe quedar cerrado, no abierto.
// MAL — abierto salvo lista negra: olvidar un caso lo deja expuesto
if (ruta === '/admin' && !user.isAdmin) return deny();
// ...todo lo demás pasa
// BIEN — cerrado salvo permiso explícito
if (!puede(user, 'leer', recurso)) return deny();
2. Autoriza a nivel de objeto (evita IDOR/BOLA)
El error más común: comprobar que el usuario está logueado pero no que el recurso le pertenece. Así nace el Insecure Direct Object Reference.
// MAL — cualquier usuario autenticado lee la factura de cualquier otro
app.get('/facturas/:id', auth, async (req, res) => {
const factura = await db.facturas.findById(req.params.id);
res.json(factura);
});
// BIEN — se filtra por el dueño del recurso
app.get('/facturas/:id', auth, async (req, res) => {
const factura = await db.facturas.findOne({
id: req.params.id,
ownerId: req.user.id,
});
if (!factura) return res.sendStatus(404);
res.json(factura);
});
3. Nunca confíes en el cliente para el rol
El rol, el isAdmin o el precio nunca deben venir del cliente y aceptarse tal cual. Decide
siempre en el servidor con datos de confianza.
// MAL — el cliente dice qué rol tiene
const { role } = req.body;
if (role === 'admin') { /* ... */ }
// BIEN — el rol se recupera del almacén de confianza
const user = await db.users.findById(req.session.userId);
if (user.role === 'admin') { /* ... */ }
4. Centraliza la lógica de autorización
Dispersar los if por los controladores garantiza inconsistencias. Usa un único punto:
middleware, policies (CASL, Oso) o un motor de políticas (OPA). Modela con RBAC (por rol)
o ABAC (por atributos) según la complejidad.
5. Protege también funciones y campos
La autorización no es solo por endpoint: aplica a acciones (¿puede aprobar?) y a campos (¿puede ver el salario?). En APIs GraphQL, autoriza por campo.
Checklist rápida
- Deny-by-default en rutas y recursos.
- Verificación de propiedad del objeto en cada acceso (anti-IDOR/BOLA).
- El rol y los privilegios se resuelven en el servidor, nunca desde el cliente.
- Lógica de autorización centralizada y testeada.
- Autorización a nivel de función y de campo, no solo de ruta.
- Tests automatizados que intentan acceder a recursos ajenos.
- #owasp
- #authz
- #idor
- #bola