Broken Object-Level Authorization (BOLA / IDOR): el fallo de API que más encontramos
Una vulnerabilidad BOLA (también llamada IDOR) es el fallo que más reportamos en las pruebas de API. Así la cazamos intercambiando los ID de objetos entre dos cuentas, y así la cerramos con autorización del lado del servidor.

Cambie un solo número en una URL y ya está leyendo la factura de otra persona. Ese es todo el truco detrás de una vulnerabilidad BOLA, y es el hallazgo que documentamos más que ningún otro en los trabajos sobre API. El endpoint se comporta a la perfección para el usuario que ha iniciado sesión. Devuelve un JSON limpio, supera cada prueba funcional y luego entrega en silencio cualquier registro que usted pida, siempre que conozca el ID.
Broken Object-Level Authorization (BOLA) es el nombre, en la era de las API, de lo que la mayoría de los ingenieros sigue llamando IDOR, una Insecure Direct Object Reference. Ocupa el primer puesto del OWASP API Security Top 10 con el código API1:2023, y se ha ganado ese puesto. Es fácil de poner en producción, sobrevive a la revisión de código y resulta invisible para un escáner que no tiene ni idea de cómo es su modelo de datos. En los backends REST y GraphQL que probamos, esta única categoría genera una gran parte de nuestros hallazgos altos y críticos.
La buena noticia es que una vulnerabilidad BOLA es también uno de los fallos más evitables de la lista una vez que conoce su forma. Este artículo explica qué es, cómo la cazamos en un trabajo real, qué aspecto tienen la petición y la respuesta en bruto, el daño que causa en producción y la corrección que de verdad aguanta.
Puntos clave
- Una vulnerabilidad BOLA (también llamada IDOR) se produce cuando una API devuelve o modifica un objeto a partir de un ID proporcionado por el cliente sin comprobar que quien llama tiene permiso para acceder a ese objeto.
- Es la OWASP API1:2023 (Broken Object Level Authorization) y se corresponde con la CWE-639, Authorization Bypass Through User-Controlled Key.
- La encontramos autenticándonos con dos cuentas distintas y repitiendo las peticiones de la cuenta A con el token de la cuenta B, intercambiando los ID de objetos y vigilando las respuestas 200 que filtran los datos del usuario equivocado.
- Los ID enteros secuenciales hacen que la enumeración masiva sea trivial, pero los UUID y los hashes no corrigen el fallo. Solo ralentizan las conjeturas.
- La única corrección fiable es una comprobación de autorización del lado del servidor en cada objeto, en cada petición, vinculada a la sesión autenticada y no a ningún ID que envíe el cliente.
¿Qué es una vulnerabilidad BOLA (y en qué se diferencia del IDOR)?
Una vulnerabilidad BOLA es la incapacidad de confirmar que quien llama, ya autenticado, tiene permiso para actuar sobre el objeto concreto que ha solicitado. La API lo autentica sin problemas. Sabe exactamente quién es usted. Luego toma un ID de objeto directamente de la petición y se salta la única pregunta que importa: ¿le pertenece este objeto?
IDOR y BOLA describen la misma causa raíz. IDOR es el término web más antiguo y amplio. BOLA es el nombre que eligió OWASP para la variante a nivel de objeto cuando construyó el API Security Top 10, porque las API exponen identificadores de objetos por todas partes y ese patrón se convirtió en el riesgo dominante de las API. Cuando escribimos “IDOR” en un informe, nos referimos exactamente a esto: la referencia al objeto es directa, la controla el usuario y está desprotegida. A lo largo de este artículo usamos ambos términos para el mismo hallazgo.
Conviene separar BOLA de su prima, la Broken Function Level Authorization (API5:2023). Los fallos a nivel de función tienen que ver con invocar una acción que no debería poder invocar en absoluto, como alcanzar una ruta reservada a administradores. Los fallos a nivel de objeto tienen que ver con invocar un endpoint perfectamente legítimo sobre datos que no son suyos. Puede tener cada ruta bloqueada y aun así perder cada ficha de cliente por un único parámetro id sin comprobar.
¿Por qué las API REST y GraphQL son tan propensas a esto?
Las API son propensas al BOLA porque se construyen en torno a objetos direccionables, y cada uno de esos objetos necesita su propia protección. Una aplicación clásica renderizada en el servidor tiende a acotar los datos a la sesión por accidente. La página solo consulta “mis” pedidos, así que no hay nada que recuperar mediante un ID arbitrario. Una API hace lo contrario. Publica una superficie uniforme como /api/orders/{id} e invita al cliente a nombrar el objeto directamente. Multiplique eso por unos cientos de endpoints y unas decenas de tipos de objetos, y la probabilidad de que uno de ellos haya olvidado su comprobación de propiedad sube deprisa.
REST deja el identificador a plena vista. Está en la ruta o en una cadena de consulta, suele ser un entero secuencial y se tarda unos diez segundos en fuzzearlo. GraphQL esconde el mismo problema en otro sitio. Un único endpoint acepta un argumento id en resolvers anidados, y la autorización debe aplicarse a nivel de campo y de nodo. Los desarrolladores que protegen la consulta de nivel superior pasan por alto de forma habitual las búsquedas anidadas node(id: ...), los alias agrupados o las aristas de conexión que resuelven objetos por su cuenta. El resultado es idéntico: pase un ID que no le pertenece y obtendrá datos que no debería ver.
Los microservicios añaden un modo de fallo más encima. En cuanto una petición cruza una frontera de confianza interna, los servicios posteriores tienden a suponer que la puerta de enlace ya se ocupó de la autorización, así que resuelven alegremente cualquier ID de objeto que les llegue.
¿Cómo cazamos una vulnerabilidad BOLA en un trabajo?
Nuestro método es deliberadamente aburrido: autenticarnos con dos cuentas distintas de bajos privilegios y luego intentar que una cuenta acceda a los datos de la otra. Lo aburrido es precisamente lo que encuentra fallos reales de forma constante. Este es el ciclo.
Primero cartografiamos la superficie de objetos. Hacemos pasar la aplicación por Burp Suite, recorremos cada funcionalidad como usuario A y catalogamos cada endpoint que acepta un identificador de objeto. ID de pedidos, ID de documentos, hilos de mensajes, números de factura, identificadores de perfil, claves de archivos. Todo lo que el cliente pueda nombrar va a la lista.
Después caracterizamos los identificadores. Los enteros secuenciales significan que un atacante puede recorrer todo el conjunto de datos contando desde 1. Los UUID, los ULID y los hashes elevan el esfuerzo, y los tratamos como un badén, no como un control. No dejamos de encontrar ID “imposibles de adivinar” que se filtran por algún otro canal: una vista de lista, un recibo por correo, un enlace de recomendación, una arista de GraphQL, una traza de pila en una respuesta de error. En cuanto el ID queda expuesto en algún sitio, su entropía deja de importar.
Luego viene el intercambio con dos cuentas. Capturamos las peticiones del usuario A y las repetimos con el token de sesión del usuario B, cambiando solo el ID de objeto para que vuelva a apuntar al recurso de A. Aquí es donde la extensión Autorize para Burp se gana su hueco en la barra de herramientas. Aliméntela con la cookie o el token bearer del usuario B y repetirá en silencio cada petición que usted haga como B, y luego etiquetará las respuestas en verde (aplicada), en rojo (eludida) o en naranja cuando no pueda decidirse y quiera que un humano lo revise. Una fila roja con los datos de A en la respuesta de B es una vulnerabilidad BOLA, y punto.
Nunca nos detenemos en el GET. Algunos de los hallazgos más feos que entregamos están en PUT, PATCH y DELETE, donde una comprobación ausente permite que un usuario sobrescriba o destruya los registros de otro. También movemos el identificador: un ID que se comprueba en la URL a veces se ignora cuando el mismo valor se coloca en un cuerpo JSON, y un campo de mass-assignment como "userId" puede reasignar en silencio el objeto a otra persona. Para la enumeración a gran escala impulsamos el parámetro de ID con ffuf o Burp Intruder y comparamos códigos de estado y longitudes de respuesta para detectar qué ID devuelven datos reales frente a un 404 uniforme.
¿Qué aspecto tiene realmente la petición vulnerable?
Aquí tiene un ejemplo anonimizado contra un objetivo ficticio. El usuario B está autenticado con su propio token válido, pero solicita el pedido 1024, que pertenece al usuario A.
GET /api/orders/1024 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.userB-token
Accept: application/json
Una API segura responde 404 Not Found o 403 Forbidden, porque el pedido 1024 no está acotado al usuario B. Una API vulnerable autentica el token, busca el pedido solo por el ID y lo serializa de vuelta tal cual:
HTTP/1.1 200 OK
Content-Type: application/json
{
"orderId": 1024,
"customer": "Alice Nguyen",
"email": "[email protected]",
"shippingAddress": "742 Evergreen Terrace",
"items": [ { "sku": "SKU-88213", "qty": 2 } ],
"total": "184.00",
"paymentLast4": "4242"
}
El token es del usuario B. Los datos son del usuario A. Nada en la respuesta está mal formado. El servidor hizo exactamente lo que el código le indicó, que era recuperar el pedido 1024 y devolverlo. Cambie 1024 por 1025, luego 1026, y ya se ha abierto camino por script a través de toda la tabla de pedidos. Ese es el fallo en su forma más pura.
¿Cuál es el impacto en el mundo real?
El impacto es una exposición masiva de datos, a menudo con una toma de control de cuentas montada encima. Como la enumeración se puede automatizar por script, un único endpoint desprotegido no filtra un solo registro. Los filtra todos. Hemos visto fallos a nivel de objeto exponer los datos personales completos de los clientes, los historiales de pedidos, los metadatos de pago, los mensajes privados y los documentos internos, todo ello alcanzable mediante un bucle que cuenta los ID mientras usted se va a por un café.
Rara vez se queda en la lectura. Si el mismo patrón vive en un endpoint de perfil o de ajustes que acepta PATCH, un atacante puede cambiar el correo electrónico o el número de teléfono de otro usuario y luego lanzar un restablecimiento de contraseña a la dirección que ahora controla, y el fallo de lectura se convierte en una toma de control completa de la cuenta. Si el objeto es una clave de API, un secreto de webhook o un token de invitación, la filtración se transforma en un punto de apoyo para un acceso más profundo. Los organismos reguladores tratan el acceso no autorizado a datos personales como una brecha por trivial que fuera la técnica, así que la factura de notificación y cumplimiento llega igual que llegaría con cualquier otra exposición.
¿Cómo se previene una vulnerabilidad BOLA?
Aplique una comprobación de autorización del lado del servidor en cada objeto, en cada petición, vinculada a la sesión autenticada y no a nada que envíe el cliente. Esa única frase es la corrección. Lo demás no es más que cómo hacerla fiable.
- Denegar por defecto. El acceso a un objeto debe fallar de forma cerrada salvo que una regla explícita indique que el principal actual puede verlo. Un endpoint nuevo debe heredar el bloqueo, no el acceso.
- Nunca trate un ID proporcionado por el cliente como una decisión de autorización. El ID le dice qué objeto. La sesión le dice quién pregunta. La autorización es la unión de ambos, y esa unión corresponde al servidor todas y cada una de las veces.
- Acote la consulta al propietario. El patrón más duradero que recomendamos es el acceso a datos acotado al objeto: consulte
WHERE order.id = :id AND order.owner_id = :currentUseren lugar de recuperar por ID y comprobar la propiedad después. Una fila que no es suya sencillamente no se carga nunca, así que no hay ningún hueco entre la carga y la comprobación que nadie pueda olvidar. - Centralice la comprobación. Ponga la autorización en una capa de políticas o un middleware por el que pasen todos los resolvers y controladores, en lugar de copiar y pegar una comprobación de propiedad en cada manejador. Las comprobaciones dispersas son justo la razón por la que se pasa por alto un endpoint.
- En GraphQL, autorice a nivel de nodo y de campo. Proteja cada resolver que cargue un objeto por ID, incluidas las búsquedas
nodeanidadas, las aristas de conexión y los alias agrupados, no solo la consulta de nivel superior. - Use ID difíciles de adivinar como defensa en profundidad, no como el control. Los UUID reducen la enumeración a ciegas, pero nunca sustituyen a la comprobación de propiedad.
Ya que está, incorpore cobertura de regresión. Una prueba que inicie sesión como usuario B y verifique un 403 o un 404 sobre el objeto del usuario A cuesta unos minutos de escribir y detecta el día en que alguien refactoriza la consulta y deja caer en silencio el acotamiento.
Cómo ayuda CyberXplore
La autorización a nivel de objeto es exactamente el tipo de fallo que los escáneres automatizados pasan por alto y que las pruebas manuales detectan, porque depende de entender su modelo de datos y de ejecutar la comparación con dos cuentas contra la lógica de negocio real. Nuestro equipo de pruebas de penetración de API cartografía cada endpoint que porta objetos, repite las peticiones entre cuentas distintas y valida cada hallazgo con una petición reproducible como la anterior, y después le entrega orientación de corrección para cerrarlo. Si desea una evaluación centrada en BOLA de su API REST o GraphQL, solicite un presupuesto y la delimitaremos en torno a sus endpoints reales.
FAQ
¿Es BOLA lo mismo que IDOR?
En la práctica, sí. IDOR (Insecure Direct Object Reference) es el término web más antiguo y general, y BOLA (Broken Object Level Authorization) es el nombre que usa OWASP para la versión a nivel de objeto en el API Security Top 10. Ambos comparten la misma causa raíz: el servidor actúa sobre un ID de objeto proporcionado por el cliente sin confirmar que quien llama está autorizado para ese objeto. Usamos ambos términos para el mismo hallazgo.
¿Dónde se sitúa BOLA en el OWASP API Security Top 10?
Es la API1:2023, Broken Object Level Authorization, la primera entrada de la lista de 2023. También se corresponde con la CWE-639, Authorization Bypass Through User-Controlled Key. Citar ambas en un informe da a los desarrolladores una referencia precisa y respaldada por estándares para esta categoría de fallo y su remediación.
¿Previenen los UUID los fallos IDOR en las API?
No. Los UUID aleatorios hacen que la enumeración a ciegas sea mucho más difícil, así que ayudan frente a un atacante ocasional que adivina ID, pero no añaden ninguna autorización. Si el ID se filtra por un endpoint de lista, un correo, un enlace de recomendación o una arista de GraphQL, cosa que ocurre con frecuencia, el objeto sigue abierto de par en par. Trate los ID imposibles de adivinar como defensa en profundidad, nunca como el control de acceso en sí.
¿Puede un escáner de vulnerabilidades encontrar BOLA?
Rara vez por sí solo. Los escáneres no saben qué objeto pertenece a qué usuario, así que no pueden detectar que una respuesta 200 de aspecto válido en realidad está filtrando los datos de otra cuenta. Encontrar BOLA de forma fiable exige pruebas autenticadas con al menos dos cuentas y una herramienta como el Autorize de Burp para comparar respuestas. Por eso se mantiene cerca de la cima de los hallazgos en las pruebas manuales de API.
¿BOLA afecta solo a las peticiones GET?
No, y las operaciones de escritura suelen ser peores. Una comprobación de objeto ausente en PUT, PATCH o DELETE permite a un atacante modificar o eliminar registros que pertenecen a otros usuarios, y un campo modificable como una dirección de correo puede encadenarse hasta una toma de control de cuenta. Probamos cada método contra cada endpoint que porta objetos, no solo las lecturas.
¿Con qué rapidez podemos corregir un hallazgo BOLA confirmado?
A menudo dentro de un sprint. La corrección más robusta consiste en acotar cada consulta de datos al propietario autenticado para que las filas no autorizadas no se carguen nunca, y en centralizar esa comprobación en una capa de políticas en lugar de en código por manejador. Añada una prueba de regresión que verifique un 403 o un 404 cuando una cuenta solicita el objeto de otra, y la categoría permanece cerrada a medida que el código sigue cambiando.



