Price Tampering : quand le serveur fait confiance au prix envoyé par le client
Le défi
Voici la requête de paiement que la boutique envoie quand vous achetez l'article #42. Regardez bien : le prix qui va vous être facturé est là, dans la requête, et le serveur lui fait confiance. Baissez le prix, envoyez la commande, et lisez la note de confirmation que la réponse laisse fuiter.
Ce que tu vas apprendre
- Reconnaître une faille de confiance côté client où le montant facturé est lu depuis un paramètre de la requête
- Modifier un unique champ numérique dans une requête de paiement capturée, puis la rejouer
- Lire une réponse HTTP/JSON pour confirmer que le prix réduit a bien été facturé
- Expliquer pourquoi les prix et autres valeurs monétaires doivent être redérivés côté serveur à partir d'une source fiable
- Distinguer la validation des entrées (rejeter les formats invalides) de l'autorité (décider de ce que doit être une valeur)
Compétences testées
Prérequis
- Compréhension de base des requêtes HTTP, des paramètres de requête et des corps JSON
- Familiarité avec le concept de panier d'achat et de tunnel de paiement
Comment ça marche
Une faille de price tampering est une vulnérabilité de logique métier : l'application place une valeur monétaire, le prix qui sera facturé à l'acheteur, dans une requête que l'acheteur contrôle, et le serveur traite cette valeur comme la source de vérité. Comme chaque octet d'une requête transite par le navigateur et par tout proxy que l'utilisateur fait tourner, l'acheteur peut simplement réécrire price=4999 en price=1 avant l'envoi de la requête. Le serveur facture ce qu'on lui a dit, et la commande se termine à un prix que le marchand n'a jamais fixé.
Le point subtil, c'est que ce n'est ni une injection ni un bug d'authentification. Le cookie de session est valide, le champ est un nombre parfaitement bien formé, et rien n'est malformé. L'erreur est une erreur d'autorité : un prix est un fait côté serveur qui appartient au catalogue, indexé par l'id de l'article. La requête devrait indiquer quel article l'acheteur veut et en quelle quantité, jamais son coût. Quand le serveur laisse le client nommer le prix, il a remis le contrôle de son chiffre d'affaires entre les mains de l'attaquant.
Ce schéma apparaît partout où un nombre fourni par le client est utilisé sans vérification pour une décision monétaire ou de quantité : remises, frais de livraison, points de fidélité, devise et taxe. La correction fiable est toujours la même : récupérer la valeur depuis une source fiable côté serveur et ignorer ce que le client a envoyé.
Erreurs fréquentes
- Supposer que le prix affiché dans la requête est fixé par le serveur, alors qu'il s'agit simplement d'un nombre dans le corps de la requête que n'importe qui peut réécrire avant l'envoi.
- Se tourner vers des caractères spéciaux ou des payloads d'injection au lieu de remarquer qu'un simple nombre modifiable décide de ce qui vous est facturé.
- Mettre le prix à
0et abandonner quand le serveur le rejette : la validation impose seulement un minimum de 1, donc la plus petite valeur acceptée est 1. - Considérer cela comme un problème de validation d'entrée plutôt que comme la vraie faille : le serveur n'aurait jamais dû accepter un prix venant du client.
Comment s'en protéger
Ne lisez jamais un prix, une remise, des frais ou tout montant monétaire depuis un paramètre de requête, un champ du corps ou un champ de formulaire caché. Côté serveur, récupérez l'article par son id dans votre catalogue ou service de tarification de confiance, calculez le total à cet endroit, et facturez ce montant : la requête ne doit décrire que ce que l'utilisateur veut acheter, jamais son coût.
- Dérivez le prix unitaire côté serveur à partir de l'id de l'article et de votre référentiel de prix ; ignorez tout champ de prix fourni par le client.
- Recalculez le total de la commande, les taxes et les frais de livraison côté serveur au moment du paiement, puis rapprochez ce total de l'autorisation de paiement.
- Validez également la quantité et l'id de l'article, et rejetez les commandes dont le total calculé ne correspond pas au montant réellement autorisé.
- Journalisez et alertez lorsqu'une requête transporte un prix qui diffère du catalogue, afin de rendre visibles les tentatives de manipulation.