BOLA en APIs: por qué sigue siendo el hallazgo nº 1 y cómo lo encadenamos hasta datos de otros clientes
Broken Object Level Authorization lidera el OWASP API Top 10 desde hace años y sigue siendo el hallazgo crítico más común en nuestros pentests de API. El mecanismo, las variantes que los escáneres no detectan y qué corrige de verdad.

Un número en el lugar equivocado
GET /api/v2/invoices/48213 devuelve la factura del usuario autenticado. GET /api/v2/invoices/48212 devuelve la de otro cliente. Sin error, sin log, sin alerta. Eso es BOLA — Broken Object Level Authorization —, el ítem API1 del OWASP API Security Top 10, el mismo problema que el mundo web llamaba IDOR.
Es el hallazgo crítico más frecuente en nuestros pentests de API, y no por descuido de los equipos: nace de una decisión de arquitectura razonable (identificar objetos por ID) combinada con una comprobación que parece existir pero está en el lugar equivocado.
Por qué los escáneres no lo encuentran
Un escáner no sabe qué es "tuyo" y qué es "de otro". Ve dos respuestas 200 con JSON válido y sigue. Detectar BOLA exige dos contextos autenticados, entender el modelo de datos y hacer la pregunta correcta: ¿este usuario debería ver esto? Eso es trabajo manual, o tooling propio que carga el contexto de identidad, que es como lo hacemos.
Las variantes que más encontramos
1. ID secuencial o predecible
El caso clásico. Entero incremental, timestamp, DNI, correo en la URL. Enumerable en minutos.
2. El UUID que se filtra en otro sitio
"Usamos UUID v4, no se puede adivinar." Cierto, pero el UUID del cliente aparece en la respuesta de /api/orders, en el enlace del correo de confirmación, en el HTML de la página pública, en el webhook al partner. La oscuridad no es autorización.
3. Autorización solo en GET, no en PATCH
El equipo protegió la lectura. La escritura (PUT, PATCH, DELETE) usa otro handler, escrito después, sin el mismo middleware. Leer es malo; cambiar la dirección de entrega o la cuenta de cobro de otro cliente es peor.
4. El ID en el cuerpo, no en la URL
POST /api/transfer con {"from_account": "...", "to_account": "..."}. El backend confía en el from_account enviado por el cliente en lugar de derivarlo de la sesión. Lo hemos visto en una fintech regulada.
5. GraphQL y batching
query { user(id: 48212) { email, documents { url } } }. Los resolvers anidados suelen comprobar la autorización solo en el nodo raíz. Con alias y batching se enumeran miles de objetos en una sola petición.
6. Endpoints "internos" o legados
/api/v1/ siguió activo después de /v2/. O /internal/export sin autenticación porque "solo lo llama el frontend de admin". El frontend no es una frontera de seguridad.
7. Autorización por tenant, no por objeto
En un SaaS multi-tenant se comprueba si el usuario pertenece a la empresa X, pero no si el objeto pertenece a la empresa X. Un usuario de X lee objetos de Y cambiando el ID.
Cómo lo encadenamos: del 200 al impacto de negocio
Un BOLA aislado es grave. Lo que el informe debe mostrar es el camino hasta el daño real:
- Lectura de la factura de otro cliente → datos personales (RGPD).
- La factura incluye el UUID del documento → descarga del PDF con datos bancarios.
- El mismo patrón en
PATCH /api/users/{id}→ cambio del correo de recuperación → account takeover. - Con la cuenta, acceso a
POST /api/payouts→ movimiento de dinero.
Cada paso es una petición documentada con request/response en el informe. La dirección no necesita entender IDOR; necesita entender "cualquier usuario podía cambiar la cuenta de cobro de cualquier otro".
Qué lo corrige de verdad
- Deriva la identidad de la sesión, nunca del input. El
from_accountviene del token, no del JSON. - Autoriza sobre el objeto, en cada operación. Un único punto (policy, middleware, repositorio) que responde "¿puede este sujeto hacer esta acción sobre este objeto?", y todos los handlers pasan por él, incluidos los resolvers de GraphQL y los jobs internos.
- Acota en la consulta, no después.
WHERE id = ? AND owner_id = ?en la base de datos. Filtrar en memoria tras traerlo todo es una invitación a la fuga. - Tests automáticos de autorización en CI: para cada endpoint, dos usuarios, un objeto de cada uno, aserción de 403/404. Evita regresiones; no sustituye al pentest, que encuentra los caminos para los que nadie escribió un test.
- Rate limit y alertas ante patrones de enumeración: no lo impide, pero acorta la ventana.
- Retira el legado. Las versiones antiguas de la API necesitan fecha de apagado, no olvido.
Qué pedir en tu próximo pentest de API
Exige que la prueba se haga con al menos dos usuarios por rol y con el OpenAPI/Swagger a mano (grey-box). Pide cobertura explícita de BOLA, BFLA (autorización a nivel de función, API5), mass assignment (API3) y de los endpoints no documentados que encuentre el recon. Y pide el encadenamiento: un informe con 30 IDOR aislados vale menos que uno con el camino hasta el takeover.
Cómo lo prueba Pentest Machine
Nuestro API Pentest se guía por el OWASP API Top 10 y por la especificación de tu API, con múltiples identidades por rol y tooling propio que carga contexto de autorización durante la enumeración, lo que permite probar miles de combinaciones objeto × usuario × operación sin perder el análisis manual de las cadenas. Cada hallazgo incluye PoC reproducible, impacto de negocio y el patrón de corrección, y el retest está incluido.