Sécurité JSON : vulnérabilités courantes et comment les éviter
JSON ne contient pas de code exécutable et échappe aux risques d'entités externes de XML comme aux surprises de coercition de types de YAML, d'où sa réputation de format sûr à analyser. L'étape d'analyse mérite presque toute cette réputation. Les vraies vulnérabilités se trouvent dans ce que votre code fait ensuite de l'objet analysé.
Pollution de prototype via une entrée JSON
Les objets JavaScript héritent d'une chaîne de prototypes partagée. Si du JSON non fiable contient une clé comme "__proto__" et que votre code la fusionne dans un objet existant sans protéger les clés spéciales, un attaquant peut injecter des propriétés sur Object.prototype lui-même. Cela touche tous les objets de votre application, pas seulement celui qui reçoit la fusion.
{
"__proto__": {
"isAdmin": true
}
}Fusionnez cela en profondeur dans un objet utilisateur avec une fusion récursive qui ne traite pas à part les clés __proto__, constructor ou prototype, et chaque objet du processus, y compris ceux qui n'ont jamais touché cette charge utile, peut se retrouver avec une propriété isAdmin à true. Des attaquants ont exploité cette classe de vulnérabilités dans plusieurs bibliothèques npm populaires de fusion/extension.
Comment éviter la pollution de prototype
Utilisez Object.create(null) pour les maps construites à partir de clés non fiables, car il n'a pas de prototype à polluer. Préférez Map aux objets simples quand les clés viennent d'une saisie utilisateur et que vous n'avez pas besoin de sérialiser le résultat en JSON. Si vous devez fusionner en profondeur du JSON non fiable dans un objet existant, utilisez un utilitaire de fusion qui bloque __proto__, constructor et prototype comme clés, et vérifiez si votre dépendance a corrigé le problème (la plupart des grandes bibliothèques l'ont fait dans les versions récentes) au lieu de le supposer. Gardez vos dépendances à jour : les mainteneurs ont corrigé cette classe de vulnérabilités à de nombreuses reprises dans des paquets populaires, à mesure que des chercheurs trouvaient de nouveaux contournements.
Désérialisation non sûre au-delà du simple JSON.parse
JSON.parse() seul produit des données brutes : chaînes, nombres, booléens, null, objets simples et tableaux. Il ne peut ni construire des instances de classes arbitraires ni exécuter de code, contrairement à d'autres formats de sérialisation (le pickle de Python peut exécuter du code arbitraire pendant la désérialisation). Le risque apparaît quand une fonction reviver ou une étape en aval reconstruit des instances de classes ou exécute de la logique à partir de valeurs de champs non fiables. Exemple : du code qui lit un champ "type" dans le JSON et s'en sert pour faire un require() de module ou un dispatch vers un gestionnaire par nom, sans vérifier la chaîne contre une liste d'autorisation.
Valider contre un schéma avant de faire confiance au JSON non fiable
JSON.parse() garantit la validité syntaxique et rien d'autre ; il ne dit pas si les données correspondent à la forme attendue par votre code. Un corps de requête en JSON valide mais privé d'un champ obligatoire, ou avec une chaîne là où vous attendiez un nombre, peut faire planter le code en aval ou produire un comportement erroné sans erreur. Validez le JSON entrant contre un schéma (JSON Schema, Zod, Joi ou équivalent) avant de l'utiliser. Vous interceptez les structures malformées à la frontière, vous les rejetez avec un message d'erreur utile au lieu d'un plantage confus trois fonctions plus loin, et le schéma documente la forme attendue par le point de terminaison.
Une liste de contrôle pratique
Validez le JSON non fiable contre un schéma à la frontière, avant qu'il n'atteigne la logique métier. Ne fusionnez pas en profondeur du JSON non fiable dans des objets durables sans un utilitaire de fusion qui bloque les clés dangereuses. Évitez les fonctions reviver ou le dispatch dynamique fondé sur des champs de chaînes non fiables, sauf si vous vérifiez les valeurs contre une liste d'autorisation. Gardez à jour les dépendances qui manipulent du JSON (analyseurs, utilitaires de fusion, validateurs de schéma), car les chercheurs continuent de trouver de nouvelles variantes de ces bogues. Fixez des limites de taille aux corps de requête JSON pour réduire l'exposition aux attaques par épuisement de ressources via des charges très imbriquées ou très volumineuses.
FAQ
- JSON.parse() lui-même est-il vulnérable à l'exécution de code, comme eval() ?
- Non. JSON.parse() ne produit jamais que des structures de données brutes (chaînes, nombres, booléens, null, objets simples, tableaux) et n'exécute jamais de code, contrairement à l'analyse JSON via eval() que de très vieilles bases de code utilisaient avant que JSON.parse() natif ne soit standard. Le JSON.parse() moderne est sûr vis-à-vis de l'exécution de code ; les risques abordés ici vivent dans ce que votre code fait du résultat analysé ensuite.
- Qu'est-ce que la pollution de prototype en termes simples ?
- Un attaquant envoie une entrée (souvent du JSON avec une clé __proto__), et du code qui ne protège pas les noms de propriétés spéciaux la fusionne dans un objet. L'attaquant peut alors ajouter ou écraser des propriétés sur l'objet de base partagé dont héritent tous les objets JavaScript, ce qui touche toute l'application.
- Ai-je besoin de validation par schéma si j'utilise déjà TypeScript ?
- Oui. Le compilateur TypeScript efface les types, qui n'offrent donc aucune protection à l'exécution. Rien ne vérifie une charge utile JSON malformée ou malveillante arrivée par HTTP, sauf si vous la validez avec une bibliothèque comme Zod, Joi ou un validateur JSON Schema. TypeScript vous empêche d'écrire du code qui manipule mal la forme attendue ; il ne peut pas vérifier que les données entrantes correspondent à cette forme.
- YAML ou XML sont-ils plus sûrs que JSON à analyser ?
- Non, ils ont leurs propres classes de vulnérabilités. Les analyseurs YAML de certains langages acceptent la coercition de types et des tags capables de construire des objets arbitraires, sauf si vous les configurez en mode « safe load » restreint. Les analyseurs XML tombent face aux attaques XXE (XML External Entity) si vous laissez l'expansion des entités active. Le système de types plus simple de JSON (pas de tags, pas d'entités) supprime ces surfaces d'attaque, mais les risques de désérialisation et de fusion décrits ici restent valables.
Essayez ces outils
Articles associés
Qu'est-ce que JSON ? Le guide complet pour débutants →
JSON (JavaScript Object Notation) est un format d'échange de données léger, facile à lire pour les humains et à analyser pour les machines. Apprenez la syntaxe, les types de données et la structure qui font tourner les API modernes.
L'histoire de JSON : de 2000 au standard de l'industrie →
Suivez JSON depuis l'idée de Douglas Crockford en 2001, en passant par son adoption par Yahoo, jusqu'à devenir le standard ECMA-404 qui fait tourner 90 % des API modernes.
Le guide complet de JSON Schema (Draft 7) →
JSON Schema est le standard pour décrire et valider la structure JSON. Apprenez les mots-clés essentiels, construisez un véritable schéma d'API et appliquez les bonnes pratiques.