L'invitation qui a créé un propriétaire : lire ce que le formulaire envoie réellement
Le défi
Vantage a découvert dans son espace de travail un compte Propriétaire que personne ne se souvient d'avoir créé. Le compte appartient à un vrai collègue, invité dans un lot ordinaire de quatre personnes, et le responsable d'équipe qui a envoyé ce lot est certain de n'avoir jamais touché à un rôle. Il dit vrai : l'écran d'invitation ne propose aucun contrôle de rôle, et il annonce noir sur blanc que toute personne invitée depuis cet écran rejoint l'espace comme Membre. Mais ce qu'une page affiche et ce qu'une page envoie sont deux documents différents, et le serveur a cru le second. Voici l'écran exactement tel qu'il a été soumis, capturé par l'outil de support. Lisez le balisage et nommez l'adresse arrivée avec le rôle Propriétaire.
Ce que tu vas apprendre
- Comparer ce qu'une page affiche à ce que son formulaire enverrait réellement
- Lire deux tableaux positionnels comme des données co-indexées
- Expliquer pourquoi un champ caché n'est ni secret ni en lecture seule
- Reconnaître l'autorisation fournie par le client comme la faille sous-jacente
- Décrire pourquoi cette élévation de privilèges laisse une trace d'audit d'apparence normale
Compétences testées
Prérequis
- Ce qu'est un formulaire HTML et un champ caché
- Lire une liste séparée par des virgules
Comment ça marche
Chaque formulaire est en réalité deux documents. Il y a celui que l'utilisateur lit, et il y a l'ensemble des paires nom-valeur que le navigateur va envoyer. Ils sont censés correspondre. Quand ce n'est pas le cas, le serveur croit presque toujours le second, car c'est le seul qu'il voit jamais.
L'aperçu est ici cohérent avec lui-même : quatre invités, le mot Membre à côté de chacun, et une phrase promettant que cet écran n'accorde rien d'autre. Il n'y a aucun contrôle de rôle à détourner, ce qui explique exactement pourquoi la version des faits du responsable d'équipe est sincère et pourtant fausse.
Voir la source contient cinq champs que la page n'a jamais dessinés. Deux d'entre eux sont des listes. invitees contient quatre adresses dans l'ordre et roles vaut member,member,owner,member. Le commentaire au-dessus énonce le contrat : roles[] est positionnel et doit rester synchronisé avec invitees[]. Alignez-les et la troisième entrée de chacun associe [email protected] à owner.
La faille n'est pas qu'un champ ait été caché. display:none et type="hidden" sont des instructions de mise en page, pas des contrôles d'accès. La valeur transite quand même sur le réseau, et n'importe qui disposant des outils de développement, d'un proxy intercepteur ou d'une simple commande curl peut la modifier. La faille est que le serveur ait accepté un rôle fourni par le client, tout simplement, si bien que la promesse imprimée sur la page n'était appliquée qu'à un seul endroit : la page.
Deux éléments rendent ce schéma pire qu'il n'y paraît. Des tableaux parallèles reliés par l'index sont fragiles, et un seul réordonnancement n'importe où dans la chaîne réattribue silencieusement tous les rôles. Et les conséquences restent invisibles : l'invitation, l'acceptation et le nouveau Propriétaire paraissent tous légitimes dans le journal d'audit, car du point de vue du serveur, un utilisateur autorisé a demandé précisément cela.
Erreurs fréquentes
- Répondre [email protected]. L'entrée
ownerest la troisième, pas la quatrième. Comptez les positions plutôt que de supposer que c'est la dernière. - Répondre avec le rôle plutôt que l'adresse. La question demande qui, et le libellé de la réponse le précise.
- Faire confiance aux lignes affichées. Les quatre indiquent Membre, ce qui montre que la page visible n'est pas une preuve.
- Considérer l'attribut caché comme la vulnérabilité. Cacher un champ ne change rien au fait que le navigateur l'envoie.
- Supposer qu'un compte privilégié est à l'origine de ceci. Un rôle fourni par le client ne nécessite aucun privilège, seulement une requête.
Comment s'en protéger
Décidez des rôles côté serveur, en fonction de qui fait la demande plutôt que de ce qui a été envoyé.
- Supprimez entièrement
rolesde la requête. Si l'écran ne peut créer que des Membres, le gestionnaire doit assigner Membre et ignorer tout rôle présent dans le corps de la requête. - Là où un rôle peut réellement être choisi, revérifiez côté serveur que l'appelant est autorisé à l'accorder, et que le rôle fait partie de l'ensemble permis pour ce plan et cette organisation.
- Liez la requête à l'organisation issue de la session, pas à un champ
orgcontrôlé par le client. - Remplacez les tableaux parallèles par une liste d'objets, une adresse et un rôle par entrée, afin que rien ne dépende de la position.
- Générez une alerte pour tout octroi de rôle Propriétaire qui ne provient pas de l'écran de gestion des rôles. Cette élévation de privilèges est silencieuse précisément parce que chaque enregistrement pris isolément paraît normal.