Désactivé mais livré : lire une action de suppression dans un formulaire désactivé
Le défi
Ce tableau de bord d'administration grise le bouton 'Supprimer l'utilisateur' pour qu'on ne puisse pas cliquer dessus. Mais le formulaire derrière est tout de même livré dans le HTML, avec son action. Ouvrez le code source, trouvez l'adresse vers laquelle l'action de suppression envoie, et soumettez-la.
Ce que tu vas apprendre
- Comprendre que désactiver un bouton est un état d'affichage côté client, pas un contrôle serveur
- Lire l'URL d'action d'un formulaire dans le code source d'une page
- Trouver l'endpoint cible derrière un contrôle grisé par l'interface
- Reconnaître que les contrôles cachés ou désactivés livrent tout de même leur cible au client
- Expliquer pourquoi l'autorisation doit être vérifiée côté serveur à chaque requête
Compétences testées
Prérequis
- Savoir ouvrir le code source dans un navigateur
- Notion de base que les formulaires envoient vers une URL (leur action)
Comment ça marche
Les interfaces web grisent ou désactivent souvent un contrôle que l'utilisateur actuel n'est pas autorisé à utiliser, par exemple un bouton Delete user grisé. Cela donne l'impression d'une mesure de sécurité, mais désactiver un bouton n'empêche qu'un clic dans ce navigateur. Le formulaire auquel appartient le bouton, y compris l'URL vers laquelle il enverrait la requête (son action), fait toujours partie du HTML envoyé à la page.
Cela signifie que l'endpoint cible est lisible dans le code source, et pire, il peut être appelé directement. Dans ce challenge, le code source montre que l'action de suppression pointe vers /admin/users/delete, avec un commentaire admettant que le bouton est seulement "disabled client-side until role=owner." Un attaquant n'a pas besoin d'activer le bouton ; il peut envoyer une requête directement à /admin/users/delete lui-même. Si le serveur fait confiance au fait que l'interface a empêché l'action, la suppression passe.
C'est un schéma classique de contrôle d'accès défaillant (broken access control) : le frontend est décoratif. Les boutons cachés, les champs désactivés et les messages "vous n'avez pas la permission" sont des indices pour l'utilisateur, pas une application réelle. La seule chose qui bloque vraiment l'action, c'est que le serveur revérifie, sur la requête elle-même, que l'appelant est autorisé à l'effectuer.
Erreurs fréquentes
- Faire confiance à l'état désactivé. Supposer qu'un bouton grisé signifie que l'action ne peut pas se produire, alors que seul le clic a été bloqué.
- Ne regarder que le bouton. Lire le libellé du bouton et manquer l'action du formulaire et le commentaire qui nomment le véritable endpoint.
- Soumettre la mauvaise route. Taper le lien de réinitialisation ou de liste au lieu de l'URL de l'action de suppression.
- Supposer que les champs cachés sont inertes. Négliger le champ caché et le commentaire qui portent
/admin/users/delete.
Comment s'en protéger
Ne traitez jamais un contrôle désactivé ou caché comme une vérification d'accès. Appliquez l'autorisation côté serveur pour chaque requête qui modifie un état, quel que soit ce que l'interface montrait.
- Revérifiez le rôle et la permission de l'appelant côté serveur, à l'intérieur même de l'endpoint de suppression, à chaque requête.
- Ne comptez pas sur la désactivation, le masquage ou le grisage des contrôles pour empêcher une action, ce sont uniquement des états d'affichage.
- Exigez que les routes qui modifient un état utilisent une méthode de requête appropriée et rejetez les appelants non autorisés, même s'ils connaissent l'URL.
- Évitez de divulguer la cible de l'action et la logique d'accès dans des commentaires et des champs cachés livrés au client.