Mass Assignment / Over-Posting : definir isAdmin depuis le corps de la requête
Le défi
Voici la requête que l'app envoie quand vous enregistrez votre profil. Elle envoie les champs affichés par le formulaire - mais le corps porte aussi un drapeau isAdmin que le formulaire n'affiche jamais, et le serveur enregistre ce qu'il reçoit. Inversez ce drapeau, envoyez, et lisez le flag admin que la réponse débloque.
Ce que tu vas apprendre
- Reconnaître le mass assignment quand un serveur lie directement le JSON du client à un modèle privilégié
- Repérer un champ sensible (isAdmin) transporté dans le corps d'une requête que l'interface n'expose jamais
- Modifier un champ booléen dans une requête capturée et la rejouer pour escalader ses privilèges
- Lire une réponse d'API pour confirmer qu'un panneau admin et des données privilégiées ont été débloqués
- Expliquer pourquoi les attributs d'autorisation doivent être définis par la logique serveur, et non liés depuis l'entrée utilisateur
Compétences testées
Prérequis
- Compréhension de base des requêtes HTTP, des corps JSON et des soumissions de formulaires
- Familiarité avec la notion de rôle admin par opposition à un utilisateur normal
Comment ça marche
Le mass assignment, aussi appelé over-posting ou auto-binding, se produit quand un framework mappe directement les champs d'un corps de requête entrant sur un objet du domaine, sans que le développeur restreigne les champs autorisés. La fonctionnalité pratique qui copie displayName depuis le JSON vers l'enregistrement utilisateur copiera tout aussi volontiers isAdmin, role, balance ou emailVerified si l'attaquant les ajoute, car le binder ne sait pas quels champs sont sensibles.
L'indice ici est que le corps de la requête porte un champ que le formulaire ne vous a jamais montré. Un formulaire de profil normal permet de modifier un nom d'affichage ; il n'affiche pas d'interrupteur admin. Mais la requête sous-jacente n'est que du JSON, entièrement modifiable, et le serveur lie l'objet entier. Mettre isAdmin à true n'exploite pas un bug de parseur - la valeur est un booléen parfaitement valide. La faille, c'est qu'un drapeau de privilège ait jamais été accessible via une liaison contrôlée par l'utilisateur.
C'est une forme d'escalade de privilèges et de contrôle d'accès défaillant. Le même schéma permet à des attaquants de vérifier leur propre email, de s'octroyer du crédit, ou de modifier l'id d'un autre utilisateur, selon les colonnes que le modèle expose. La défense robuste consiste à ne lier qu'une liste blanche explicite des champs modifiables par l'utilisateur, et à définir chaque attribut d'autorisation via une logique serveur de confiance.
Erreurs fréquentes
- Supposer que la requête ne peut contenir que les champs affichés par le formulaire, alors que le corps est du JSON brut que l'on peut librement modifier ou compléter.
- Essayer des payloads d'injection sur le nom d'affichage au lieu de remarquer le drapeau de privilège booléen présent dans le corps.
- Envoyer une valeur non booléenne comme
1ouyeset abandonner quand le serveur la rejette - le champ est validé comme un booléen, donc la valeur doit êtretrue. - Considérer cela comme un bug d'interface plutôt que la véritable faille : le serveur ne devrait jamais lier un champ de privilège depuis la requête.
Comment s'en protéger
Ne liez qu'une liste blanche explicite des champs qu'un utilisateur est autorisé à modifier, et définissez les attributs d'autorisation uniquement via la logique côté serveur. Le corps de la requête ne devrait jamais pouvoir écrire dans une colonne de privilège - si isAdmin arrive dans le JSON, ignorez-le au lieu de l'appliquer.
- Utilisez une liste blanche côté serveur (ou un DTO d'entrée dédié) listant exactement les champs modifiables, comme
displayName; ignorez tout le reste dans le corps. - N'exposez jamais les colonnes sensibles (
isAdmin,role,balance,emailVerified) à l'auto-binder ; ne les définissez que via des chemins de code contrôlés et audités. - Appliquez une vérification d'autorisation distincte pour les changements de rôle - accorder le statut admin doit exiger un acteur admin, pas une simple sauvegarde de profil en libre-service.
- Journalisez et alertez lorsqu'une requête tente de définir un champ privilégié, afin de rendre visibles les tentatives d'over-posting.