Assez souvent lorsque l'on contrôle une application: lors d'un audit, un pentest, ou un bug bounty on trouve tout de même souvent des gros échecs au niveau de la gestion des contrôles d'accès. Ce n'est pas pour rien que l'Access Control est maintenant le #1 de l'OWASP TOP 10. Je vais tenter ici de faire un petit point sur les bonnes pratiques à mettre en place pour assurer une bonne sécurité à ce niveau.
Oui je sais je m'attaque à un gros morceau mais je suis comme ça.
Rien d'exhaustif, mais tout de même pas mal de points. Je me base sur plusieurs choses, le retour d'expérience, l'OWASP ASVS, du troll (non ce n'est pas le genre de la maison), et les bonnes pratiques de manière générale.
Pensez le contrôle d'accès au niveau de l'architecture logiciel
Et oui pas de miracle: une architecture non pensée pour la sécurité va vous créer tout de même pas mal de problèmes en terme de sécurité par la suite.
Beaucoup de points sont à prévoir dans l'architecture logicielle mais si vous voulez éviter de gros problèmes par la suite, voici tout de même quelques bonnes pratiques:
- Déjà, cela paraît basique mais IL NE FAUT SURTOUT PAS faire les contrôles de sécurité côté client;
- N'utilisez pas des milliers de mécanismes de contrôle d'accès, choisissez une méthode et assurez-vous que tous les appels passent par celui-ci.
- Assurez vous que TOUTES les actions passent par le mécanisme de contrôle d'accès et que ce dernier contrôle les droits:
- De l'utilisateur
- Du rôle lié à l'utilisateur
- Générez des logs exploitables vin dious (c'est du Picard si vous vous posez la question :))
- Merci d'utiliser un format standard
- Et dans l'architecture merci de penser à envoyer les logs à un puits de log, voir directement à un SIEM !
- Pensez à définir votre type de contrôle d'accès:
- RBAC: Role Based Access Control: bon celui-ci est assez simple à comprendre: vous définissez des rôles et ce sont de ces rôles dont dépendent les droits d'accès des utilisateurs;
- ABAC: Attribute Based Access Control: en gros chaque demandeur et chaque objet de la demande ont des attributs et c'est par rapport à ces attributs que l'on octroie, ou pas des droits d'accès. A noter que dans certaines circonstance on peut se baser sur les rôles aussi dans ce contexte, mais pas uniquement. En gros les question à se poser sont:
- Qui ?
- Quoi ?
- Quand ?
- Dans quel contexte ?
- ReBAC: Relationship Based Access Control: Là typiquement on se base sur les relations entre les demandeurs et les objets. En gros: si un utilisateur crée quelque chose sur votre application, il peut y avoir accès en lecture/écriture, alors que quelqu'un ayant le même role que lui pourtant, n'y aura pas nécéssairement accès, mais que quelqu'un de son équipe (ou de sa famille) pourra y avoir accès en lecture. Voilà en gros ça c'est du ReBAC. En gros pensez au fonctionnement d'un réseau social type facebook.
Bon ok ça c'est fait ! on fait quoi maintenant ?
Bah on implémente en essayant de contrôler que tout est bien en place pour cela il y a "quelques" points à contrôler.
Alors nous verrons par la suite, comment contrôler et être sûr que les équipes de développement peuvent tout mettre en oeuvre, mais déjà parcouront un peu ce qu'il faut contrôler.
- En plus des points déjà cité plus haut, il faut checker que aucune des données utilisées pour effectuer le contrôle d'accès côté serveur ne peut être modifiée côté client ! Ca parait évident, mais malheureusment ça arrive encore trop souvent;
- Utilisez les principe du "least privilege" et de l'interdiction par défaut (deny by default): donc souvent lorsque l'on parle de cela c'est comment dire, ..., assez vague. Mais en gros ceci est lié à ce que j'ai expliqué quelques lignes plus haut: les *BAC. Lorsqu'une donnée est créée par défaut son accès doit être interdit à toutes et tous, de même pour une fonction spécifique. Et ensuite autorisé selon: le rôle, la relation ou les spécificités de l'entité qui en fait la demande (les fameux *BAC);
- Alors dans le "concret", en gros un bon vieux test technique à faire dans un pentest, vérifiez que l'on ne peut accéder à un object via un appel direct ! C'est con mais c'est aussi un type de vulnérabilités assez connue et commune;
- Bon c'est plus côté authentification: mais mettez du MFA sur vos pages d'administration;
- Enfin: protégez vous contre les CSRF car bon si on peut bypasser vos protections en piégeant un utilisateur, bien sûr que l'on va le faire :)
T'es bien marrant toi, mais comment je teste et je vérifie moi ? Avec mon quel outil ?
Alors l'Access Control avec des outils comment dire, les outils c'est bien mais ...
Clairement, bien sûr que vous devez faire des contrôles automatiques dans tout votre SDLC: SAST, DAST, WAF, API Security tools, SCA, j'en passe et des meilleurs. Mais soyons clair: si les outils de sécurité résolvez, ou détectez TOUS les problèmes cela ferez à minima 15 ans que la cybersécurité ne serait plus un sujet. Et non ce n'est pas le dernier outil ML/AI/je sais trop quoi/Blockchain based et military graded qui va changer ça. Surtout pour les contrôles d'accès.
Non malheureusement la bonne solution est encore une fois humaine:
- Revue d'architecture
- Revue de code
- Threat modeling et Analyses des risques
- Pentest
Pas trop le choix, et toutes sont nécéssaires !
Donc voilà quelques pistes de choses que vous devez traquer lors de toutes ces étapes:
- Déjà est-ce que les points dont je viens de parler ont été traité dans l'architecture et la phase de design de l'application ? Si la réponse est non: bah ... forcément vous allez trouver des choses.
- Ensuite la chose à tester ce sont les directory traversal: alors là des outils genre SAST/DAST peuvent effectivement vous aider. Mais globalement:
- Détecter les points d'entrées possibles
- Tester avec différents types d'entrée du style: répertoires, URLs, partages réseaux, ...
- Mais ce point de protection reste tout de même un sujet de protection des entrées fournies à l'application, donc je ne vais pas rentrer dans le détail. Par contre une conséquence peut être le bypass des protections de contrôles d'accès ! Du coup pensez-y.
- Le bypass de l'authentification: là, peu importe le contexte (analyses des risques, pentest, ...) vous devez vous poser les questions suivantes:
- Est-ce que je peux accéder à une ressource d'un utilisateur A depuis le compte d'un utilisateur B ?
- C'est assez simple à tester: vous créez deux comptes, vous créez une donnée sur le compte A, vous regardez comment on y accède (id dans l'URL ? Appel API spécifique ? ...) et vous testez avec le compte B. Si ça marche il y a un petit fail dans le potage (oui j'aurai pu mettre un autre mot).
- De même pour les fonctions d'administration: est-ce que l'on peut s'en servir sans être administrateur. Les tests se font exactement de la même manière hein.
- Et enfin est-ce que l'on peut accéder aux ressources lorsque l'on est pas loggué, ou "déloggué" ? (même principe de test encore une fois)
- Et vous pouvez voir que aucun produit ne sera réellement capable de détecter ce genre de fail, ou alors TRES difficilement avec beaucoup de faux positifs.
- Est-ce que je peux accéder à une ressource d'un utilisateur A depuis le compte d'un utilisateur B ?
- La protection contre les escalations de privilèges: alors là aussi les outils peuvent en détecter, mais en gros le but c'est de modifier des valeurs afin de vous faire passer pour quelqu'un d'autre, ou de changer votre rôle: données de sessions, modifier des données envoyées lors d'un appel, des données techniques comme des IP dans X-Formarded-For, ...
- IDOR j'en ai déjà parlé quelques fois :).
Conclusions
- L'architecture logicielle n'est plus à la mode et c'est bien dommage: pensez une bonne architecture logicielle évite pas mal les erreurs de contrôle d'accès (et bien d'autres aussi)
- Sensibilisez vos développeurs sur le sujet (et sur de nombreux autres)
- S-SDLC donc les outils mais aussi Threat modeling, revue d'architecture, analyses des risques, pentest, ...
Voilà, ce texte est bien sûr en license CC BY-SA 4.0 (legal).