Un Broken Access Control est une défaillance de l'autorisation applicative : un utilisateur accède à des ressources ou déclenche des actions qui devraient lui être refusées selon la politique de sécurité. Classée A01 du OWASP Top 10 depuis 2021 et confirmée au premier rang dans la version 2025 (100 % des applications testées présentent au moins une instance), cette catégorie regroupe les IDOR (Insecure Direct Object Reference), BOLA (Broken Object Level Authorization, OWASP API #1 en 2023), BFLA (Broken Function Level Authorization), les escalades verticales et horizontales, le force browsing, et les contournements de workflow métier. Elle se caractérise par une requête syntaxiquement valide mais sémantiquement non autorisée - ce qui la rend invisible à un WAF et très difficile à détecter par SAST. Cet article détaille les sous-types, les mécanismes d'exploitation, les exemples historiques (Optus 2022, Instagram, Facebook), les patrons de correction en Python/Node/Java, et les stratégies de prévention durables.
Définition et position OWASP
Le OWASP A01:2021 Broken Access Control couvre l'ensemble des cas où une application échoue à appliquer correctement ses règles d'autorisation. Il ne s'agit pas d'un contournement d'authentification - l'utilisateur est bien authentifié - mais d'une faille dans la vérification des droits après identification.
Chiffres clés 2021 vs 2025
| Métrique | OWASP 2021 | OWASP 2025 |
|---|---|---|
| Position dans le Top 10 | #1 | #1 |
| Applications testées présentant des instances | 94 % | 100 % |
| Taux moyen d'incidence | 3,81 % | en hausse |
| Occurrences dans le dataset | 318 000+ | ordre de grandeur supérieur |
| CWEs associés | 34 | 30+ |
Source : OWASP Top 10:2021 et Top 10:2025 RC1.
CWEs principaux couverts
CWE-284 : Improper Access Control
CWE-285 : Improper Authorization
CWE-639 : Authorization Bypass Through User-Controlled Key (IDOR)
CWE-862 : Missing Authorization
CWE-863 : Incorrect Authorization
CWE-276 : Incorrect Default Permissions
CWE-732 : Incorrect Permission Assignment for Critical Resource
CWE-200 : Exposure of Sensitive Information to an Unauthorized Actor
CWE-352 : Cross-Site Request Forgery (CSRF - sous-famille)
CWE-918 : Server-Side Request Forgery (SSRF - sous-famille)Les sous-types de Broken Access Control
La catégorie A01 regroupe des variantes aux signatures techniques distinctes.
IDOR (Insecure Direct Object Reference)
L'application expose un identifiant d'objet directement manipulable par l'utilisateur (dans l'URL, le body, les paramètres) sans vérifier que l'utilisateur demandeur est bien propriétaire ou autorisé.
Exemple classique :
GET /api/invoice/4218
Utilisateur A (id=17) accède à sa facture 4218 légitimement.
Utilisateur B (id=42) change 4218 en 4219 et récupère
la facture de l'utilisateur A sans vérification serveur.BOLA (Broken Object Level Authorization)
Nom officiel d'IDOR dans le contexte API REST / GraphQL. Classé #1 du OWASP API Security Top 10 2023. Même mécanisme qu'IDOR mais formalisé pour les APIs.
BFLA (Broken Function Level Authorization)
L'attaquant accède à une fonction ou un endpoint qui aurait dû être réservé à un rôle plus élevé. Typiquement : un endpoint /api/admin/users accessible par un compte utilisateur standard.
Exemple BFLA :
Utilisateur normal authentifié appelle :
DELETE /api/admin/users/42
Aucun middleware ne vérifie le rôle admin sur cet endpoint.
L'utilisateur supprime des comptes.Escalade verticale vs horizontale
Horizontale : utilisateur A accède aux ressources de l'utilisateur B
(même rôle, ressources différentes)
→ typiquement IDOR/BOLA
Verticale : utilisateur standard accède à des fonctions admin
(rôle supérieur usurpé)
→ typiquement BFLA ou escalade de privilègeForce browsing
L'attaquant devine ou énumère des URLs non référencées publiquement mais accessibles sans contrôle : pages admin, fichiers de backup, endpoints de debug.
Exemples de paths à tester systématiquement :
/admin/, /admin.php, /debug, /backup.zip, /.git/config
/api/v1/internal/, /phpinfo.php, /.env, /swagger.json
/actuator/ (Spring Boot Actuator), /wp-admin/Mass Assignment et binding automatique
Un framework lie automatiquement les champs d'un body JSON à un objet modèle. Si l'application ne filtre pas explicitement les champs autorisés, un attaquant injecte des champs privilégiés.
POST /api/profile
{
"name": "Alice",
"email": "alice@example.com",
"role": "admin", ← injection malveillante
"is_active": true
}
Si le code bind aveuglément le body à l'objet User,
l'utilisateur devient admin.Contournement de workflow métier
L'attaquant saute des étapes dans un processus (paiement, validation, KYC) en accédant directement aux endpoints avals.
Workflow attendu :
1. POST /cart/add
2. POST /checkout/validate
3. POST /payment/process
4. POST /order/confirm
Contournement :
Appel direct à POST /order/confirm
sans passer par /payment/process si pas de vérif serveur.Cas réels et incidents publics
Optus (Australie, septembre 2022)
IDOR sur endpoint API client
Exfiltration de 9,8 millions de dossiers clients
Amende et class action estimés >150 M AUD
Instagram (2019)
IDOR sur stories et posts privés
Accès à contenus privés via manipulation d'ID
Bug bounty : 30 000 USD
Facebook (2019-2020)
Multiples IDORs sur API Graph
Bounties cumulés >100 000 USD
Uber (2019)
Prise de compte via IDOR sur reset password
Bug bounty : 6 500 USD
Shopify (2020)
IDOR permettant accès à données marchands
Bug bounty : 25 000 USD
T-Mobile (2021)
Accès non autorisé via API à 40+ millions de dossiers
Classé A01 par les analystes post-incidentMécanismes de défense
RBAC - Role-Based Access Control
Les utilisateurs se voient attribuer des rôles (admin, manager, user, guest) et chaque ressource ou action définit le rôle minimum requis. Simple à implémenter, limité sur des contextes fins.
# Python Flask - RBAC simple via décorateur
from functools import wraps
def requires_role(role):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
if g.current_user is None or role not in g.current_user.roles:
abort(403)
return f(*args, **kwargs)
return wrapper
return decorator
@app.route("/api/admin/users")
@requires_auth
@requires_role("admin")
def list_users():
return jsonify(User.query.all())ABAC - Attribute-Based Access Control
Autorisation basée sur des attributs (utilisateur, ressource, action, contexte). Plus expressif que le RBAC pour les cas fins type « un manager peut voir les rapports de son équipe mais pas des autres équipes ».
ReBAC - Relationship-Based Access Control
Basé sur les relations (propriétaire, membre, collaborateur). Popularisé par Google Zanzibar (2019) et ses implémentations open source : OpenFGA (CNCF), SpiceDB, Warrant.
Authorization centralisée
Approche recommandée pour les applications non triviales : extraire la logique d'autorisation dans un service ou une librairie dédiée plutôt que de la disperser dans chaque endpoint.
Options 2026 :
OPA (Open Policy Agent) : standard CNCF, policies en Rego
Cedar (AWS, 2023+) : langage dédié, très lisible
OpenFGA : ReBAC CNCF
Casbin : multi-langages
Librairie maison : viable si scope simpleCode vulnérable vs corrigé par langage
Python / Flask
# VULNÉRABLE - IDOR sur facture
@app.route("/api/invoice/<invoice_id>")
@requires_auth
def get_invoice(invoice_id):
return jsonify(db.get_invoice(invoice_id))
# CORRIGÉ - vérification de propriété
@app.route("/api/invoice/<invoice_id>")
@requires_auth
def get_invoice(invoice_id):
invoice = db.get_invoice(invoice_id)
if invoice is None or invoice.owner_id != g.current_user.id:
abort(404) # 404 plutôt que 403 pour éviter énumération
return jsonify(invoice)Node.js / Express
// VULNÉRABLE - Mass Assignment
app.patch('/api/profile', requireAuth, async (req, res) => {
const user = await User.findById(req.user.id);
Object.assign(user, req.body); // tous les champs bindés
await user.save();
res.json(user);
});
// CORRIGÉ - whitelist explicite
app.patch('/api/profile', requireAuth, async (req, res) => {
const ALLOWED = ['name', 'email', 'avatar_url'];
const updates = Object.fromEntries(
Object.entries(req.body).filter(([k]) => ALLOWED.includes(k))
);
const user = await User.findByIdAndUpdate(req.user.id, updates, { new: true });
res.json(user);
});Java / Spring Boot
// VULNÉRABLE - BFLA sans contrôle rôle
@DeleteMapping("/api/admin/users/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
// CORRIGÉ - Spring Security annotation
@DeleteMapping("/api/admin/users/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
// ENCORE MIEUX - vérification objet
@GetMapping("/api/invoice/{id}")
@PostAuthorize("returnObject.ownerId == authentication.principal.id or hasRole('ADMIN')")
public Invoice getInvoice(@PathVariable Long id) {
return invoiceService.findById(id);
}Détection : outils et tests
Tests automatisés par endpoint
Pour chaque endpoint protégé, créer 3 tests minimum :
Test 1 : utilisateur non authentifié → 401/403
Test 2 : utilisateur A accède à sa ressource → 200
Test 3 : utilisateur B accède à ressource de A → 404/403
Pour chaque endpoint réservé à un rôle :
Test 4 : utilisateur sans le rôle → 403
Test 5 : utilisateur avec le rôle → 200Outils de test d'autorisation 2026
| Outil | Type | Usage |
|---|---|---|
| Burp Suite + extension Autorize | Proxy web | Test automatisé IDOR cross-user |
| Burp Suite + AuthMatrix | Proxy web | Matrice de permissions |
| OWASP ZAP + Access Control Testing | Open source | Test automatisé |
| Semgrep règles p/owasp-top-ten | SAST | Détection patterns risqués |
| CodeQL queries community | SAST sémantique | Requêtes dédiées A01 |
| APIsec, Salt Security, Noname | Commercial API | Runtime API authorization testing |
Challenges SAST
Détecter Broken Access Control par SAST est fondamentalement limité : l'outil ne connaît pas l'intention métier de l'application. Les SAST détectent bien :
- Les endpoints sans middleware d'authentification visible.
- Les patterns de query DB sans filtre utilisateur.
- Les binders automatiques de framework sans whitelist.
Ils détectent mal :
- Les règles d'autorisation métier complexes.
- Les workflow contournables.
- Les escalades contextuelles (un manager d'équipe X qui accède à l'équipe Y).
D'où la nécessité combinée SAST + tests unitaires d'autorisation + pentest manuel + design review.
Prévention : les 10 règles à appliquer
- Refuser par défaut (deny by default) : tout endpoint sans autorisation explicite retourne 403.
- Centraliser l'autorisation : un service ou une librairie dédiée, pas des
ifdispersés. - Vérifier la propriété avant tout accès : chaque requête sur un objet vérifie que
object.owner_id == current_user.idou règle équivalente. - Whitelister les champs updatables : jamais de
Object.assign(model, req.body)aveugle. - Désactiver le listing de répertoire côté serveur web et cloud.
- 404 plutôt que 403 sur ressource non autorisée pour limiter l'énumération.
- Invalider les sessions côté serveur après logout et changement de rôle.
- Rate-limiter par utilisateur sur les endpoints sensibles pour ralentir l'énumération.
- Logger les échecs d'autorisation avec contexte (user_id, target_id, endpoint) pour détecter les tentatives.
- Tester l'autorisation par endpoint en tests automatisés, systématiquement.
Checklist développeur avant merge
Pour chaque PR touchant un endpoint ou une fonction sensible :
- Cet endpoint a-t-il un middleware d'authentification ?
- Cet endpoint vérifie-t-il le rôle ou la permission requise ?
- Cet endpoint vérifie-t-il la propriété de l'objet accédé ?
- Les champs modifiables sont-ils explicitement whitelistés ?
- Les IDs exposés dans l'URL sont-ils tolérables en cas de fuite ?
- Y a-t-il des tests unitaires d'autorisation (cas autorisé et refusé) ?
- Les échecs d'autorisation sont-ils loggés avec contexte ?
- La réponse d'erreur est-elle cohérente (404 vs 403) ?
Points clés à retenir
- Broken Access Control reste #1 du OWASP Top 10 de 2021 à 2025, avec 100 % des applications testées concernées dans la version 2025.
- Trois sous-types dominants en 2026 : IDOR (accès objet horizontal), BFLA (fonction verticale non autorisée), Mass Assignment (champs privilégiés bindés). BOLA est l'équivalent IDOR côté API REST.
- Aucun WAF, framework ou SAST ne remplace le contrôle applicatif explicite. L'autorisation est par nature métier et doit être vérifiée côté serveur sur chaque requête.
- Patron de correction universel : refuser par défaut, vérifier propriété avant tout accès, whitelister les champs updatables, centraliser la logique d'autorisation (OPA, Cedar, OpenFGA).
- Test systématique à automatiser : pour chaque endpoint protégé, tester accès propriétaire (200) et accès tiers (404). Burp Autorize, AuthMatrix, OWASP ZAP Access Control Testing pour les tests larges.
Pour resituer cette vulnérabilité dans la famille OWASP complète, voir introduction au OWASP Top 10. Pour comprendre pourquoi un développeur doit investir dans la maîtrise de ces catégories, lire importance du OWASP Top 10 pour les développeurs. Pour un plan de progression complet du code review sécurisé vers l'AppSec Engineering, la roadmap AppSec Engineer 2026 détaille les 4 étapes.




