Tema 8
En una API, autenticar es solo el comienzo. El verdadero riesgo suele aparecer después, cuando el sistema debe decidir qué puede hacer esa identidad sobre qué recurso, en qué contexto y con qué alcance. La autorización segura es la diferencia entre una API que reconoce usuarios y una API que realmente controla el acceso.
Muchas APIs tienen autenticación correcta y aun así sufren incidentes graves porque autorizan mal. El sistema reconoce al actor, pero no valida adecuadamente si ese actor debería poder ver, modificar, borrar o ejecutar una acción específica sobre un recurso concreto.
Esto ocurre porque la autorización es más compleja que la autenticación. No solo depende de quién es el usuario, sino también del recurso, la operación, el rol, el tenant, el contexto de negocio, el canal, el momento y, a veces, la relación entre múltiples entidades.
Por eso este tema se centra en cómo modelar autorización de forma segura en APIs REST, evitando los errores que luego terminan en BOLA, BFLA, exposición de propiedades sensibles o abuso funcional.
La autorización es el proceso por el cual la API decide si una identidad autenticada puede realizar una operación determinada sobre un recurso dado en un contexto específico. No es una sola verificación. Suele involucrar varias preguntas al mismo tiempo:
Una forma útil de pensar el problema es distinguir dos niveles:
Muchas APIs implementan solo el nivel grueso. Verifican que el usuario “tenga acceso al módulo”, pero no validan ownership, estado del recurso o campos permitidos. Ahí aparecen buena parte de las fallas graves.
Los roles son una forma común de agrupar capacidades. En vez de asignar permisos uno por uno a cada usuario, se define un conjunto de roles como `admin`, `operador`, `cliente`, `soporte` o `auditor`, y se asocia cada rol a ciertas acciones.
Esto simplifica la administración, pero tiene límites. Los roles funcionan bien para permisos relativamente estables y fáciles de agrupar. Cuando el dominio se vuelve complejo, confiar solo en roles suele llevar a dos problemas:
Los permisos expresan acciones concretas, por ejemplo: `usuarios.leer`, `usuarios.editar`, `facturas.descargar`, `pedidos.cancelar`, `reportes.exportar`. Son más precisos que los roles porque permiten modelar capacidades específicas.
En muchos sistemas, los roles se usan como agrupadores de permisos. Eso suele ser una buena práctica, porque combina administración relativamente simple con granularidad razonable.
La clave es que la API tome decisiones a nivel de permiso real, no solo a nivel de etiqueta de rol.
Los scopes aparecen frecuentemente en sistemas con OAuth 2.0 y tokens. Representan el alcance del acceso concedido a un cliente o sesión. Un scope puede indicar que un token está autorizado para leer perfiles, consultar pedidos o administrar ciertos recursos.
Los scopes ayudan a limitar tokens y a expresar intención de acceso, pero no deberían verse como sustituto universal de la autorización completa. Un token con scope `pedidos:leer` todavía necesita pasar controles sobre qué pedidos puede ver realmente ese usuario o cliente.
RBAC, o Role-Based Access Control, es uno de los modelos más usados. La lógica es directa: el acceso se decide según el rol o conjunto de roles del actor.
RBAC es útil cuando:
Sin embargo, RBAC se queda corto cuando el acceso depende de ownership, tenant, proyecto, horario, canal, ubicación o estado del negocio.
ABAC, o Attribute-Based Access Control, toma decisiones usando atributos del actor, del recurso y del contexto. Por ejemplo, un usuario puede editar un documento si pertenece al mismo tenant, si es dueño del documento o si el documento sigue en estado borrador.
Este modelo es más expresivo que RBAC y suele representar mejor las necesidades reales de una API compleja. También es más difícil de implementar y probar si no se diseña con disciplina.
En la práctica, muchas APIs usan una mezcla: roles para una primera capa y atributos/contexto para la decisión fina.
La primera capa de control suele estar en el endpoint o la función. Aquí la API decide si un actor puede usar una operación general: por ejemplo, acceder a `/admin/reportes` o ejecutar `/usuarios/{id}/desactivar`.
Errores comunes en este nivel:
Incluso si el actor puede llamar al endpoint, todavía hay que decidir si puede operar sobre ese objeto concreto. Este es el núcleo de problemas como BOLA.
Ejemplos:
La verificación debe realizarse en backend y sobre el objeto real, no asumir que el identificador recibido ya corresponde a algo legítimo para ese actor.
No todo campo de un recurso debe ser visible o editable por todos los actores que pueden acceder al objeto. Aquí entran decisiones más finas:
Una API puede estar bien protegida a nivel de endpoint y objeto, pero seguir siendo insegura si permite leer flags internos, límites de cuenta, descuentos reservados o cambiar propiedades sensibles como `role`, `status` o `is_verified`.
Muchas decisiones de acceso dependen del estado del recurso. Por ejemplo, un pedido puede editarse mientras está pendiente, pero no después de facturado. Un documento puede borrarse en borrador, pero no cuando ya fue firmado. Un turno puede cancelarse hasta cierta hora, pero no después.
Si la API ignora el estado de negocio y solo revisa rol o permiso, abre la puerta a abuso funcional y operaciones incoherentes con las reglas del sistema.
En sistemas multi-tenant, la autorización debe incluir aislamiento entre organizaciones, cuentas o espacios de trabajo. Un usuario puede tener permiso para operar dentro de un tenant y no en otro, aunque su rol sea el mismo.
Problemas frecuentes en este punto:
El principio de mínimo privilegio indica que cada actor debe tener solo los permisos necesarios para cumplir su función, y nada más. En APIs, esto se traduce en varios niveles:
El problema práctico es que muchas implementaciones sacrifican precisión para “simplificar”. Esa simplificación suele acumular deuda de seguridad y terminar en privilegios excesivos difíciles de retirar después.
La autorización puede aparecer en varios niveles: gateway, middleware, controlador, servicio de negocio o motor de políticas. Ninguna ubicación por sí sola resuelve todo.
| Nivel | Ventaja | Límite |
|---|---|---|
| Gateway | Aplica reglas generales comunes | No suele conocer contexto fino del recurso |
| Middleware | Estandariza chequeos iniciales | Puede quedarse corto para lógica compleja |
| Servicio de negocio | Ve estado real y reglas del dominio | Puede dispersarse si no se centraliza criterio |
| Motor de políticas | Centraliza y hace más auditable la decisión | Agrega complejidad y requiere buena gobernanza |
En general, los controles gruesos pueden estar antes; las decisiones finas deben vivir cerca del dominio real.
La autorización no debería verificarse solo con pruebas felices. Conviene probar sistemáticamente combinaciones de actor, recurso y operación.
Casos clave:
Las pruebas de autorización deben cubrir no solo rutas, sino también payloads, filtros, relaciones y propiedades.
Una API madura no solo toma decisiones de autorización. También puede explicarlas y auditarlas. Esto no significa exponer toda la lógica al cliente, sino registrar contexto suficiente para investigar incidentes y entender por qué una operación fue permitida o denegada.
Es útil registrar:
La autorización segura es uno de los pilares más críticos de una API REST porque traduce identidad en decisiones reales de acceso. Diseñarla bien exige ir más allá de roles generales y considerar permisos, scopes, ownership, tenants, propiedades sensibles y estado del negocio. Cuando esta capa se simplifica demasiado, la API puede estar correctamente autenticada pero profundamente expuesta.
En el próximo tema profundizaremos en dos fallas especialmente peligrosas: Broken Object Level Authorization y Broken Function Level Authorization, para ver cómo aparecen en la práctica y cómo prevenirlas.