Trois minutes, un chiffre, des milliers de dossiers
Trois minutes. C’est le temps qu’il nous a fallu pour accéder aux dossiers de tous les locataires d’une plateforme française de gestion locative. Pas d’outil sophistiqué, pas de payload, pas de génie technique. On a juste compté.
Le client : une PropTech française. Le terme désigne ces startups qui appliquent les codes du SaaS au monde de l’immobilier (gestion locative, signature de baux en ligne, intermédiation entre propriétaires et locataires, le tout via une plateforme web unique). Le produit qu’on devait auditer : leur portail locataire, là où chaque utilisateur consulte ses propres documents : bail, quittances, RIB, pièce d’identité.
Pour l’audit, le client nous fournit ce qu’il donne à n’importe quel nouvel arrivant : un compte. Un identifiant, un mot de passe, notre propre dossier. Rien de plus.
On se connecte. On voit nos documents. Et dans la barre d’adresse, ceci :
# Notre dossier de locataire
https://espace.locagestion.fr/dossier?id=4815
# On change un seul chiffre
https://espace.locagestion.fr/dossier?id=4816
# Et voici le dossier du locataire suivant
Vous voyez le 4815 à la fin de l’URL ? C’est l’identifiant de notre dossier. Un numéro. Qui se suit. Alors on a fait la chose la plus simple du monde : on a remplacé 4815 par 4816.
Le dossier d’un autre locataire s’est affiché. Son bail, ses quittances, son RIB. On a continué avec 4817, puis 4818. À chaque fois, un nouveau locataire. À chaque fois, ses documents les plus sensibles.
En quelques minutes, on aurait pu aspirer toute la base. Des milliers de dossiers, à travers toutes les agences clientes de la plateforme. Juste en comptant.
Ce qui s’est joué ici porte un nom : une IDOR (Insecure Direct Object Reference, soit une référence à un objet non sécurisée). Elle appartient à la catégorie A01 Broken Access Control de l’OWASP Top 10, tout simplement la catégorie numéro un du classement depuis 2021.
Et avant d’aller plus loin, soyons clairs : il n’a fallu aucun outil, aucun code, aucune compétence rare. Votre stagiaire pourrait tomber dessus par accident. La seule chose dont on a eu besoin, c’est de savoir compter. Et c’est ce qui rend ce type de faille à la fois si répandue et si dévastatrice.
Ce n’est d’ailleurs pas la première fois qu’on documente publiquement ce genre de scénario. On en avait déjà retrouvé une variante dans notre retour d’audit sur 9bank, où une IDOR similaire ouvrait l’accès aux RIB d’autres clients de la banque.
L’analogie du pressing
Imaginez un pressing. Vous déposez votre chemise, on vous remet un ticket avec un numéro : 42. Pour récupérer votre vêtement, vous revenez, vous présentez le ticket 42, et l’employé va chercher en arrière-boutique ce qui correspond.
Le système entier repose sur une hypothèse implicite : celui qui présente le ticket 42 est forcément le propriétaire de la chemise 42.
Maintenant, présentez le ticket 43 à la place. L’employé ne vous demande pas si c’est bien le vôtre. Il ne regarde pas votre visage. Il lit le numéro, va chercher, et vous tend la chemise de quelqu’un d’autre.
Voilà ce qu’est une faille de contrôle d’accès. Le pressing, c’est votre application. Le ticket, c’est l’identifiant qui voyage dans la requête. L’employé vérifie bien que vous avez un ticket valide (vous êtes un client, vous êtes connecté). Il ne vérifie jamais que ce ticket est bien le vôtre.
Le système confond deux questions très différentes :
- Êtes-vous un utilisateur légitime ? C’est l’authentification.
- Avez-vous le droit d’accéder à cette donnée précise ? C’est l’autorisation.
La première, toutes les applications la posent. La seconde, beaucoup l’oublient. Tout se joue là.
Dans le code, la différence tient à quelques lignes :
// Vulnérable : on renvoie le dossier sans vérifier à qui il appartient
app.get("/dossier", (req, res) => {
const dossier = db.dossiers.get(req.query.id)
res.send(dossier)
})
// Corrigé : on vérifie que le dossier appartient à l'utilisateur connecté
app.get("/dossier", (req, res) => {
const dossier = db.dossiers.get(req.query.id)
if (dossier.locataireId !== req.user.id) {
return res.status(403).send("Accès refusé")
}
res.send(dossier)
})
Le code corrigé ajoute une seule chose : la vérification que le dossier demandé appartient bien à l’utilisateur connecté. Trois lignes. C’est tout ce qui séparait cette PropTech d’une fuite massive.
Quand le chiffre n’est pas dans l’URL
Tout cela paraît si simple qu’on serait tenté de se dire : « il suffit de cacher nos identifiants de l’URL ». Sauf que la barre d’adresse n’est qu’un cas particulier, le plus visible.
Dans beaucoup d’applications, l’identifiant qui désigne la donnée que vous consultez voyage ailleurs : dans le corps d’une requête POST envoyée en arrière-plan, dans un en-tête HTTP, dans un cookie, parfois dans plusieurs endroits à la fois. Vous ne le voyez pas dans la barre d’adresse, mais il est bien là, et le serveur l’utilise pour servir la donnée.
Pour le repérer et le manipuler, un pentester utilise un proxy d’interception. Les deux outils les plus connus sont Burp Suite et Caido. Le principe : ils se placent entre votre navigateur et le serveur, et permettent d’intercepter chaque requête qui circule, de la modifier à la volée, puis de la renvoyer. Tout ce que l’application envoie devient visible, vraiment tout, y compris ce qui reste caché à un utilisateur lambda.
Dans la pratique, on commence par utiliser l’application normalement. On consulte son propre dossier, on clique, on remplit des formulaires. Pendant ce temps, le proxy enregistre tout. Voici à quoi ressemble une requête capturée, avant et après modification :
# Requête capturée par le proxy d'interception
POST /api/dossier HTTP/1.1
Host: espace.locagestion.fr
{ "dossierId": 4815 }
# Même requête, modifiée et rejouée
POST /api/dossier HTTP/1.1
Host: espace.locagestion.fr
{ "dossierId": 4816 }
Le principe reste exactement le même que dans notre exemple initial : le serveur reçoit une requête contenant un identifiant, et ne vérifie pas que cet identifiant désigne quelque chose qui appartient à l’utilisateur connecté. Simplement, l’attaquant peut avoir besoin d’un peu plus que sa barre d’adresse pour exploiter la faille.
Et c’est précisément ce qui rend ce type de vulnérabilité si difficile à détecter pour vos propres équipes. Si l’identifiant n’apparaît pas en clair dans une URL, personne ne pensera à le manipuler. Il faut le regard d’un pentester habitué à fouiller toutes les couches de la communication entre le navigateur et le serveur.
« Nos identifiants ne sont pas devinables » : le faux ami
Réflexe classique en comité de direction : « Chez nous, les identifiants ne sont pas des numéros qui se suivent. On utilise des codes aléatoires, impossibles à deviner. Ce cas ne nous concerne pas. »
Sauf que rendre la porte difficile à trouver, ce n’est pas la verrouiller.
Un identifiant “impossible à deviner” circule en permanence, par des chemins ordinaires :
- Les journaux techniques de vos serveurs et de vos outils tiers (analytics, monitoring, support).
- L’historique de navigation, les e-mails, les liens partagés entre collègues.
- Tout ancien utilisateur ou ancien employé qui a eu un accès légitime à une URL.
- L’URL qu’un client copie-colle dans un message au support.
Le jour où ce code “secret” passe entre de mauvaises mains, s’il n’y a aucune vérification d’autorisation derrière, il donne accès à la donnée. L’obscurité n’a jamais été un contrôle d’accès. Elle repousse juste l’échéance.
Le vrai critère, ce n’est pas la prédictibilité de vos identifiants. C’est : votre application vérifie-t-elle, sur chaque requête, que la donnée demandée appartient bien à l’utilisateur connecté ? Pour la grande majorité des applications, la réponse honnête est : pas partout.
Authentification ou autorisation ? L’authentification, c’est prouver qui vous êtes (votre login). L’autorisation, c’est vérifier ce que vous avez le droit de faire une fois connecté. Une faille de contrôle d’accès, c’est presque toujours une autorisation manquante derrière une authentification réussie.
Combien coûte un défaut d’autorisation
Un défaut de contrôle d’accès qui mène à une fuite, ça se chiffre. Et plus directement qu’on ne le croit.
Le RGPD pose le cadre depuis 2018 : en cas de violation de données personnelles, notification à la CNIL sous 72 heures, et sanctions pouvant atteindre 4 % du chiffre d’affaires annuel mondial ou 20 millions d’euros, le plus élevé des deux s’appliquant.
Ces chiffres ne sont plus théoriques. Les violations de données notifiées à la CNIL ont atteint 5 629 cas en 2024, +20 % par rapport à 2023 (rapport annuel 2024 de la CNIL). Le cumul des amendes prononcées en France est passé de 55 millions d’euros en 2024 à 486,8 millions en 2025 (bilan officiel des sanctions 2025).
Le cas le plus parlant pour notre sujet ? La sanction de France Travail en janvier 2026 : 5 millions d’euros d’amende, après une fuite touchant 36,8 millions de personnes. Parmi les manquements relevés noir sur blanc par la CNIL : les habilitations d’accès avaient été définies de manière trop large (communiqué officiel CNIL, délibération SAN-2026-003 sur Légifrance). Autrement dit : un défaut de contrôle d’accès, exactement la famille dans laquelle s’inscrit notre IDOR.
L’amende n’est qu’une partie du coût. Une sanction CNIL est publique. Pour une PropTech qui vend de la confiance à des agences immobilières, voir son nom associé à une fuite de baux et de pièces d’identité, c’est un coût commercial direct, qui se concrétise au moment du renouvellement des contrats.
Effet domino. Une fuite de pièces d’identité, de RIB et de justificatifs ne reste jamais cantonnée à votre système. Ces documents alimentent dans les mois qui suivent des usurpations d’identité, des fraudes locatives et des tentatives de phishing dirigées contre vos clients.
Pourquoi cette faille survit à toutes les bonnes pratiques
Broken Access Control est la catégorie numéro un de l’OWASP Top 10. Ce n’est pas un hasard. Quatre raisons l’expliquent.
L’autorisation, c’est de la logique métier, pas de l’infra. Les technologies modernes gèrent l’authentification de façon transparente. Le login, la session, le mot de passe oublié, tout est fourni par les frameworks. Mais vérifier qu’une donnée appartient bien à un utilisateur, c’est de la logique applicative. Il faut l’écrire pour chaque endpoint, chaque action, chaque ressource. Et il suffit d’en oublier une.
Les tests valident le scénario heureux. Une équipe teste naturellement que sa fonctionnalité marche : « est-ce que mon dossier s’affiche bien ? ». Beaucoup plus rarement le scénario hostile : « est-ce que je peux accéder au dossier de quelqu’un d’autre ? ». L’un est obligatoire pour la mise en prod, l’autre demande un état d’esprit d’attaquant.
La vélocité produit. Dans une scale-up, chaque sprint ajoute des fonctionnalités. Chaque fonctionnalité ouvre une nouvelle porte. Et chaque nouvelle porte demande sa propre serrure, qu’on oublie facilement dans le rush.
Le code généré sans relecture sécurité. Quand une équipe laisse une IA générer du code (ce qu’on appelle le vibe coding), le résultat fonctionne pour l’utilisateur légitime. La vérification d’autorisation, elle, est presque toujours absente. Personne ne l’a demandée, l’IA ne l’a pas ajoutée.
La vérification que les outils ne savent pas faire
Sur les failles de contrôle d’accès, il y a un mur que la plupart des équipes ne voient pas : un outil automatique ne sait pas ce qui est légitime.
Quand un scanner voit la page dossier?id=4816 répondre normalement, il n’a aucun moyen de savoir si c’est attendu ou non. Il ne connaît pas la règle métier « ce dossier appartient au locataire X ». Pour lui, une requête qui répond avec un statut 200, c’est une requête qui marche. Les IDOR passent donc largement sous son radar.
La revue de code interne aide, à condition d’être systématique : vérifier que chaque endpoint qui retourne une donnée contient bien sa propre vérification d’autorisation. Sur une application qui a grandi vite, c’est un audit colossal, et un seul oubli suffit.
La seule méthode vraiment fiable, c’est le test manuel avec plusieurs comptes, mené par quelqu’un qui sait fouiller. Un pentester crée deux comptes distincts, puis vérifie systématiquement : est-ce que le compte A peut atteindre les données du compte B, dans n’importe quel endroit de l’application ? Sur tous les endpoints, dans tous les paramètres, qu’ils soient dans l’URL ou ailleurs (corps de requête, en-tête, cookie). C’est exactement ce qu’on a fait sur la PropTech, avec Caido en proxy d’interception. Simple dans le principe, demandant dans l’exécution.
La méthodologie d’un audit sérieux s’appuie sur des référentiels publics. Côté OWASP, l’Application Security Verification Standard (ASVS) consacre une section entière au contrôle d’accès, le Web Security Testing Guide détaille les tests d’autorisation à mener, et l’Authorization Cheat Sheet résume les bons réflexes côté développeur. Côté classification, le pattern qu’on a exploité dans cet audit correspond à la CWE-639, Authorization Bypass Through User-Controlled Key. Et côté français, l’ANSSI publie ses propres recommandations en sécurité applicative à consulter en complément. Ça garantit qu’aucune porte n’est oubliée.
Sur l’audit de cette PropTech, la faille était en production depuis le lancement du portail locataire. Plusieurs mois. Personne en interne ne l’avait jamais remarquée. Parce que personne n’avait pensé à changer un chiffre dans une requête.
Conclusion
Une faille de contrôle d’accès, c’est l’exemple presque idéal de la vulnérabilité qu’on sous-estime. Aucun code, aucun outil sophistiqué, aucune compétence rare. Un chiffre modifié dans une requête, et les données de tous vos clients défilent.
C’est précisément cette simplicité qui la rend dangereuse. Elle est tellement basique qu’on a du mal à croire qu’elle existe chez soi. Tellement basique qu’on ne pense jamais à la tester.
Si vous opérez une application qui manipule des dossiers clients, des comptes ou des documents (que vous soyez dans la PropTech, la FinTech, l’EdTech ou tout autre secteur), la question n’est pas de savoir si une attaque serait sophistiquée. Elle est : avez-vous déjà vérifié, vraiment vérifié, qu’un utilisateur ne peut pas accéder aux données d’un autre ?
C’est notre travail à chaque audit. Et on trouve, presque systématiquement, une porte que personne n’avait pensé à fermer.