IDOR : quand changer un chiffre dans l'URL donne accès aux données des autres
Une API REST bien construite, un token JWT valide, et des IDs séquentiels dans les URLs. Le reste est de l'arithmétique. Ce cas illustre la confusion la plus répandue entre authentification et autorisation : deux mécanismes distincts que beaucoup d'applications implémentent à moitié.
Contexte
Une startup SaaS propose une plateforme de prise de rendez-vous pour des cabinets de conseil. Chaque praticien gère ses propres clients via l'interface. Les données en jeu : noms, emails, numéros de téléphone, motifs de consultation, créneaux réservés.
L'audit a été demandé par un investisseur avant la signature d'un tour de financement, un rapport de sécurité faisait partie des conditions. Mission de deux jours, boite grise : un compte standard de praticien fourni, aucun accès au code source.
La découverte
En examinant le trafic de l'application après connexion, les requêtes vers l'API suivent un pattern régulier : GET /api/appointments/4821. L'identifiant est numérique et visiblement séquentiel.
Je tente d'accéder à /api/appointments/4820, puis /api/appointments/4819. Les réponses arrivent normalement : nom complet du client, email, numéro de téléphone, motif de consultation, praticien concerné. Des rendez-vous qui n'appartiennent pas au compte avec lequel je suis connecté.
Le serveur vérifiait que la requête contenait un token JWT valide : c'est l'authentification. Il ne vérifiait pas que le rendez-vous demandé appartenait au praticien authentifié : c'est l'autorisation. Les deux sont nécessaires. Une seule était implémentée.
L'impact
Tout utilisateur authentifié pouvait accéder à l'ensemble des rendez-vous de la plateforme (tous praticiens confondus), en itérant sur les IDs. Pas d'outil spécialisé requis : une simple boucle for suffit.
- Exposition des données personnelles de tous les clients de la plateforme
- Motifs de consultation potentiellement sensibles (contexte médical ou juridique selon les cabinets)
- Violation RGPD avec obligation de notification de l'autorité compétente
- Risque de fuite avant levée de fonds, exactement le scénario que l'investisseur cherchait à éviter
S'authentifier prouve qui vous êtes. Être autorisé prouve ce à quoi vous avez le droit d'accéder. Ce sont deux choses différentes, et l'une ne remplace pas l'autre.
La correction
Le correctif est architectural : chaque endpoint qui retourne une ressource doit vérifier que l'utilisateur authentifié est bien le propriétaire de cette ressource, ou dispose d'un droit explicite dessus. Ce n'est pas une ligne de code ajoutée ici ou là, c'est un contrôle systématique.
La bonne approche : lors de chaque requête GET /api/appointments/{id}, le backend récupère le rendez-vous, compare son champ owner_id avec l'ID de l'utilisateur présent dans le token JWT, et retourne 403 si les deux ne correspondent pas. Une logique centrale, testée unitairement, appliquée sur chaque endpoint concerné.
Le retest après correction a confirmé que les accès croisés étaient bloqués. L'investisseur a reçu son rapport avec la vulnérabilité documentée, la correction vérifiée, et le statut « résolu ». La levée a pu se conclure.
Ce qu'on retient
L'IDOR (Insecure Direct Object Reference) figure régulièrement dans le top 10 OWASP depuis des années. Il résiste parce que les frameworks modernes facilitent l'authentification mais n'imposent pas l'autorisation au niveau objet : c'est au développeur de l'implémenter. Sans revue externe, elle passe souvent inaperçue dans les tests fonctionnels, qui vérifient ce que l'application est censée faire, pas ce qu'elle ne devrait pas permettre.
Des IDs aléatoires (UUID v4) rendraient l'énumération plus difficile, mais ne remplacent pas une vérification d'autorisation réelle. L'obscurité n'est pas de la sécurité : si un attaquant récupère un ID par un autre canal, le problème reste entier.
Pour aller plus loin
Une question après la lecture ?
Vous vous posez des questions sur la gestion des accès dans votre API ? Envoyez un message. Pas de pitch, pas de devis automatique.
Ou réservez directement un cadrage de 30 minutes
Réserver un appel →