Broken Object-Level Authorization (BOLA / IDOR) : la faille d’API que nous rencontrons le plus
Une vulnérabilité BOLA (aussi appelée IDOR) est la faille que nous signalons le plus souvent lors des tests d’API. Voici comment nous la traquons en échangeant les ID d’objets entre deux comptes, et comment la neutraliser grâce à une autorisation côté serveur.

Changez un seul chiffre dans une URL et vous voilà en train de lire la facture de quelqu’un d’autre. C’est tout le principe d’une vulnérabilité BOLA, et c’est le constat que nous documentons plus que tout autre lors des missions sur API. Le point de terminaison se comporte parfaitement pour l’utilisateur connecté. Il renvoie un JSON propre, passe chaque test fonctionnel, puis livre discrètement n’importe quel enregistrement que vous demandez, du moment que vous en connaissez l’ID.
Broken Object-Level Authorization (BOLA) est le nom, à l’ère des API, de ce que la plupart des ingénieurs appellent encore IDOR, une Insecure Direct Object Reference. Elle occupe la première place de l’OWASP API Security Top 10 sous le code API1:2023, et ce classement est mérité. Elle est facile à mettre en production, elle survit à la revue de code et elle reste invisible pour un scanner qui n’a aucune idée de la forme de votre modèle de données. Sur l’ensemble des backends REST et GraphQL que nous testons, cette seule catégorie représente une large part de nos constats élevés et critiques.
Le bon côté, c’est qu’une vulnérabilité BOLA compte aussi parmi les failles les plus faciles à éviter de la liste, une fois que vous en connaissez la forme. Cet article explique de quoi il s’agit, comment nous la traquons lors d’une mission réelle, à quoi ressemblent la requête et la réponse brutes, les dégâts qu’elle cause en production et le correctif qui tient réellement la route.
Points clés à retenir
- Une vulnérabilité BOLA (aussi appelée IDOR) survient lorsqu’une API renvoie ou modifie un objet à partir d’un ID fourni par le client, sans vérifier que l’appelant a le droit d’accéder à cet objet.
- Il s’agit de l’OWASP API1:2023 (Broken Object Level Authorization), qui correspond à la CWE-639, Authorization Bypass Through User-Controlled Key.
- Nous la trouvons en nous authentifiant avec deux comptes distincts et en rejouant les requêtes du compte A avec le jeton du compte B, en échangeant les ID d’objets et en guettant les réponses 200 qui divulguent les données du mauvais utilisateur.
- Les ID entiers séquentiels rendent l’énumération de masse triviale, mais les UUID et les hachages ne corrigent pas la faille. Ils ne font que ralentir les tentatives.
- Le seul correctif fiable est une vérification d’autorisation côté serveur sur chaque objet, à chaque requête, rattachée à la session authentifiée plutôt qu’à un quelconque ID envoyé par le client.
Qu’est-ce qu’une vulnérabilité BOLA (et en quoi diffère-t-elle de l’IDOR) ?
Une vulnérabilité BOLA, c’est l’incapacité à confirmer que l’appelant authentifié a le droit d’agir sur l’objet précis qu’il a demandé. L’API vous authentifie sans problème. Elle sait exactement qui vous êtes. Puis elle prend un ID d’objet directement dans la requête et saute la seule question qui compte : cet objet vous appartient-il ?
IDOR et BOLA décrivent la même cause racine. IDOR est le terme web le plus ancien et le plus large. BOLA est le nom qu’a retenu l’OWASP pour la variante au niveau de l’objet lorsqu’il a bâti l’API Security Top 10, car les API exposent des identifiants d’objets partout et ce schéma est devenu le risque dominant côté API. Quand nous écrivons “IDOR” dans un rapport, nous entendons précisément ceci : la référence à l’objet est directe, contrôlée par l’utilisateur et non protégée. Tout au long de cet article, nous employons les deux termes pour désigner le même constat.
Il est utile de distinguer BOLA de sa cousine, la Broken Function Level Authorization (API5:2023). Les failles au niveau de la fonction consistent à appeler une action que vous ne devriez pas pouvoir appeler du tout, comme atteindre une route réservée aux administrateurs. Les failles au niveau de l’objet consistent à appeler un point de terminaison parfaitement légitime sur des données qui ne sont pas les vôtres. Vous pouvez avoir verrouillé chaque route et laisser malgré tout fuir chaque fiche client par un seul paramètre id non vérifié.
Pourquoi les API REST et GraphQL y sont-elles si exposées ?
Les API sont exposées au BOLA parce qu’elles sont construites autour d’objets adressables, et chacun de ces objets a besoin de sa propre protection. Une application classique rendue côté serveur a tendance à cantonner les données à la session par accident. La page n’interroge jamais que “mes” commandes, il n’y a donc rien à récupérer par un ID arbitraire. Une API fait l’inverse. Elle publie une surface uniforme comme /api/orders/{id} et invite le client à nommer directement l’objet. Multipliez cela par quelques centaines de points de terminaison et quelques dizaines de types d’objets, et la probabilité que l’un d’eux ait oublié sa vérification de propriété grimpe vite.
REST met l’identifiant en pleine lumière. Il se trouve dans le chemin ou dans une chaîne de requête, c’est souvent un entier séquentiel, et il faut environ dix secondes pour le fuzzer. GraphQL cache le même problème ailleurs. Un unique point de terminaison accepte un argument id sur des resolvers imbriqués, et l’autorisation doit être appliquée au niveau du champ et du nœud. Les développeurs qui protègent la requête de premier niveau manquent régulièrement les recherches imbriquées node(id: ...), les alias groupés ou les arêtes de connexion qui résolvent les objets de leur côté. Le résultat est identique : transmettez un ID qui ne vous appartient pas, obtenez des données que vous ne devriez pas voir.
Les microservices ajoutent encore un mode de défaillance par-dessus. Dès qu’une requête franchit une frontière de confiance interne, les services en aval ont tendance à supposer que la passerelle a déjà géré l’autorisation, et ils résolvent donc allègrement n’importe quel ID d’objet qui se présente à eux.
Comment traquons-nous une vulnérabilité BOLA lors d’une mission ?
Notre méthode est volontairement ennuyeuse : s’authentifier avec deux comptes distincts à faibles privilèges, puis tenter de faire accéder un compte aux données de l’autre. C’est justement l’ennui qui déniche des failles réelles de manière régulière. Voici la boucle.
D’abord, nous cartographions la surface d’objets. Nous faisons transiter l’application par Burp Suite, parcourons chaque fonctionnalité en tant qu’utilisateur A et répertorions chaque point de terminaison qui accepte un identifiant d’objet. ID de commandes, ID de documents, fils de messages, numéros de facture, identifiants de profil, clés de fichiers. Tout ce que le client peut nommer va sur la liste.
Ensuite, nous caractérisons les identifiants. Des entiers séquentiels signifient qu’un attaquant peut parcourir tout le jeu de données en comptant à partir de 1. Les UUID, les ULID et les hachages augmentent l’effort, et nous les considérons comme un ralentisseur, pas comme un contrôle. Nous ne cessons de constater que des ID “impossibles à deviner” fuient par un autre canal : une vue en liste, un accusé de réception par e-mail, un lien de parrainage, une arête GraphQL, une trace de pile dans une réponse d’erreur. Dès que l’ID est exposé quelque part, son entropie n’a plus d’importance.
Vient ensuite l’échange à deux comptes. Nous capturons les requêtes de l’utilisateur A et les rejouons avec le jeton de session de l’utilisateur B, en ne changeant que l’ID d’objet pour qu’il pointe de nouveau vers la ressource de A. C’est là que l’extension Autorize pour Burp justifie sa place dans la barre d’outils. Donnez-lui le cookie ou le jeton bearer de l’utilisateur B et elle répète en silence chaque requête que vous effectuez en tant que B, puis étiquette les réponses en vert (appliquée), en rouge (contournée) ou en orange lorsqu’elle ne parvient pas à trancher et veut qu’un humain y jette un œil. Une ligne rouge contenant les données de A dans la réponse de B est une vulnérabilité BOLA, point final.
Nous ne nous arrêtons jamais au GET. Certains des constats les plus laids que nous livrons portent sur PUT, PATCH et DELETE, où une vérification manquante permet à un utilisateur d’écraser ou de détruire les enregistrements d’un autre. Nous déplaçons aussi l’identifiant : un ID vérifié dans l’URL est parfois ignoré lorsque la même valeur est glissée dans un corps JSON, et un champ de mass-assignment comme "userId" peut discrètement réattribuer l’objet à quelqu’un d’autre. Pour l’énumération à grande échelle, nous pilotons le paramètre d’ID avec ffuf ou Burp Intruder et comparons les codes de statut et les longueurs de réponse pour repérer quels ID renvoient de vraies données par opposition à un 404 uniforme.
À quoi ressemble réellement la requête vulnérable ?
Voici un exemple anonymisé contre une cible fictive. L’utilisateur B est authentifié avec son propre jeton valide, mais il demande la commande 1024, qui appartient à l’utilisateur A.
GET /api/orders/1024 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.userB-token
Accept: application/json
Une API sûre répond 404 Not Found ou 403 Forbidden, car la commande 1024 n’est pas rattachée à l’utilisateur B. Une API vulnérable authentifie le jeton, recherche la commande uniquement par son ID et la sérialise directement en retour :
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"
}
Le jeton est celui de l’utilisateur B. Les données sont celles de l’utilisateur A. Rien dans la réponse n’est malformé. Le serveur a fait exactement ce que le code lui a demandé, à savoir récupérer la commande 1024 et la renvoyer. Remplacez 1024 par 1025, puis 1026, et vous avez parcouru par script toute la table des commandes. C’est la faille dans sa forme la plus pure.
Quel est l’impact concret ?
L’impact, c’est une exposition massive de données, souvent doublée d’une prise de contrôle de compte. Parce que l’énumération peut être scriptée, un seul point de terminaison non protégé ne divulgue pas un unique enregistrement. Il les divulgue tous. Nous avons vu des failles au niveau de l’objet exposer l’intégralité des données personnelles des clients, les historiques de commandes, les métadonnées de paiement, les messages privés et les documents internes, le tout accessible via une boucle qui compte les ID pendant que vous allez vous chercher un café.
Cela s’arrête rarement à la lecture. Si le même schéma se trouve sur un point de terminaison de profil ou de paramètres qui accepte PATCH, un attaquant peut modifier l’adresse e-mail ou le numéro de téléphone d’un autre utilisateur, puis déclencher une réinitialisation de mot de passe vers l’adresse qu’il contrôle désormais, et la faille de lecture devient une prise de contrôle complète du compte. Si l’objet est une clé d’API, un secret de webhook ou un jeton d’invitation, la fuite se transforme en point d’appui pour un accès plus profond. Les autorités de régulation considèrent tout accès non autorisé à des données personnelles comme une violation, quelle que soit la trivialité de la technique, de sorte que la facture de notification et de conformité arrive au même titre que pour n’importe quelle autre exposition.
Comment prévenir une vulnérabilité BOLA ?
Appliquez une vérification d’autorisation côté serveur sur chaque objet, à chaque requête, rattachée à la session authentifiée plutôt qu’à quoi que ce soit envoyé par le client. Cette seule phrase est le correctif. Le reste explique simplement comment le rendre fiable.
- Refuser par défaut. L’accès à un objet doit échouer en mode fermé tant qu’une règle explicite n’indique pas que le principal courant a le droit de le voir. Un nouveau point de terminaison doit hériter du blocage, pas de l’accès.
- Ne traitez jamais un ID fourni par le client comme une décision d’autorisation. L’ID vous indique quel objet. La session vous indique qui demande. L’autorisation est la jointure entre les deux, et cette jointure revient au serveur à chaque fois.
- Restreignez la requête au propriétaire. Le schéma le plus durable que nous recommandons est l’accès aux données restreint à l’objet : interrogez
WHERE order.id = :id AND order.owner_id = :currentUserau lieu de récupérer par ID puis de vérifier la propriété après coup. Une ligne qui ne vous appartient pas ne se charge tout simplement jamais, il n’y a donc aucun intervalle entre le chargement et la vérification que quiconque pourrait oublier. - Centralisez la vérification. Placez l’autorisation dans une couche de politiques ou un middleware que chaque resolver et chaque contrôleur traverse, plutôt que de copier-coller une vérification de propriété dans chaque gestionnaire. Des vérifications éparpillées sont exactement la raison pour laquelle un point de terminaison passe à travers les mailles.
- En GraphQL, autorisez au niveau du nœud et du champ. Protégez chaque resolver qui charge un objet par ID, y compris les recherches
nodeimbriquées, les arêtes de connexion et les alias groupés, pas seulement la requête de premier niveau. - Utilisez des ID difficiles à deviner comme défense en profondeur, pas comme le contrôle. Les UUID réduisent l’énumération à l’aveugle, mais ils ne remplacent jamais la vérification de propriété.
Intégrez au passage une couverture de non-régression. Un test qui se connecte en tant qu’utilisateur B et vérifie un 403 ou un 404 sur l’objet de l’utilisateur A prend quelques minutes à écrire et détecte le jour où quelqu’un refactorise la requête et laisse discrètement tomber la restriction.
Comment CyberXplore vous aide
L’autorisation au niveau de l’objet est exactement le genre de faille que les scanners automatisés manquent et que les tests manuels détectent, car elle repose sur la compréhension de votre modèle de données et sur la comparaison à deux comptes exécutée contre une véritable logique métier. Notre équipe de tests d’intrusion d’API cartographie chaque point de terminaison porteur d’objets, rejoue les requêtes entre des comptes distincts et valide chaque constat au moyen d’une requête reproductible comme celle ci-dessus, puis vous remet des recommandations de correction pour la refermer. Si vous souhaitez une évaluation de votre API REST ou GraphQL centrée sur le BOLA, demandez un devis et nous la cadrerons autour de vos points de terminaison réels.
FAQ
BOLA est-il la même chose que l’IDOR ?
Dans les faits, oui. IDOR (Insecure Direct Object Reference) est le terme web le plus ancien et le plus général, et BOLA (Broken Object Level Authorization) est le nom qu’utilise l’OWASP pour la version au niveau de l’objet dans l’API Security Top 10. Les deux partagent la même cause racine : le serveur agit sur un ID d’objet fourni par le client sans confirmer que l’appelant est autorisé pour cet objet. Nous employons les deux termes pour le même constat.
Où se situe BOLA dans l’OWASP API Security Top 10 ?
C’est l’API1:2023, Broken Object Level Authorization, la toute première entrée de la liste 2023. Elle correspond aussi à la CWE-639, Authorization Bypass Through User-Controlled Key. Citer les deux dans un rapport donne aux développeurs une référence précise et adossée à des standards pour cette catégorie de faille et sa remédiation.
Les UUID empêchent-ils les failles IDOR dans les API ?
Non. Les UUID aléatoires rendent l’énumération à l’aveugle beaucoup plus difficile, ils aident donc face à un attaquant de passage qui devine des ID, mais ils n’ajoutent aucune autorisation. Si l’ID fuit par un point de terminaison de liste, un e-mail, un lien de parrainage ou une arête GraphQL, ce qui arrive fréquemment, l’objet reste grand ouvert. Considérez les ID impossibles à deviner comme une défense en profondeur, jamais comme le contrôle d’accès lui-même.
Un scanner de vulnérabilités peut-il trouver un BOLA ?
Rarement à lui seul. Les scanners ne savent pas quel objet appartient à quel utilisateur, ils ne peuvent donc pas déceler qu’une réponse 200 d’apparence valide divulgue en réalité les données d’un autre compte. Trouver un BOLA de façon fiable exige des tests authentifiés avec au moins deux comptes et un outil comme l’Autorize de Burp pour comparer les réponses. C’est pourquoi il reste en tête des constats lors des tests d’API manuels.
BOLA ne concerne-t-il que les requêtes GET ?
Non, et les opérations d’écriture sont souvent pires. Une vérification d’objet manquante sur PUT, PATCH ou DELETE permet à un attaquant de modifier ou de supprimer des enregistrements qui appartiennent à d’autres utilisateurs, et un champ modifiable comme une adresse e-mail peut s’enchaîner en prise de contrôle de compte. Nous testons chaque méthode contre chaque point de terminaison porteur d’objets, pas seulement les lectures.
En combien de temps pouvons-nous corriger un constat BOLA confirmé ?
Souvent en l’espace d’un sprint. Le correctif le plus robuste consiste à restreindre chaque requête de données au propriétaire authentifié afin que les lignes non autorisées ne se chargent jamais, et à centraliser cette vérification dans une couche de politiques plutôt que dans du code par gestionnaire. Ajoutez un test de non-régression qui vérifie un 403 ou un 404 lorsqu’un compte demande l’objet d’un autre, et la catégorie reste refermée à mesure que le code continue d’évoluer.



