flawopen.com/Pollution de prototype/JSON.parse()
JSON.parse() provoque-t-il une pollution de prototype ?
Non. JSON.parse() crée une propriété propre nommée __proto__ ; l’analyse n’appelle pas le setter hérité et ne modifie aucun prototype. Le risque apparaît plus tard si un autre code copie cette clé par un setter, fusionne récursivement des clés non fiables ou écrit dans un chemin d’objet contrôlé par un attaquant.
// JSON.parse crée des données ; il ne pollue pas un prototype. const input = '{"__proto__":{"isAdmin":true}}'; const parsed = JSON.parse(input); // Object.assign utilise Set sur la cible. Ici, il modifie seulement // le prototype de cet objet local de démonstration. const copied = Object.assign({}, parsed); Object.getPrototypeOf(copied).isAdmin; // true Object.prototype.isAdmin; // undefined
// Analyser puis construire uniquement les champs acceptés. const input = '{"theme":"dark","locale":"en","__proto__":{"isAdmin":true}}'; const parsed = JSON.parse(input); const config = { theme: parsed.theme, locale: parsed.locale, }; // Pour les dictionnaires non fiables, éviter les setters hérités. const options = Object.assign(Object.create(null), parsed);
Pourquoi JSON.parse() n’est pas le point de pollution
Une clé JSON et le setter de prototype d’un littéral d’objet sont deux opérations différentes. L’analyse JSON définit des propriétés propres sur la nouvelle valeur ; elle n’exécute pas obj.__proto__ = value. Les opérations suivantes peuvent agir autrement : Object.assign(target, source) affecte la cible ; une clé propre __proto__ peut donc appeler son setter hérité. Cela modifie le prototype de l’objet cible ; à lui seul, ce mécanisme ne modifie pas Object.prototype.
Après const o = JSON.parse('{"__proto__":{"a":1}}'), Object.hasOwn(o, "__proto__") renvoie vrai et Object.prototype.a reste indéfini. Une copie ultérieure vers une cible ordinaire peut donner un autre prototype à cette cible. La syntaxe spread crée des propriétés propres sans appeler le setter, mais ne protège pas contre les fusions récursives ou les chemins d’affectation non sûrs.
Où se trouve le véritable risque
Le JSON analysé est une donnée non fiable, pas un prototype pollué. Le risque dépend des points d’écriture suivants : fusion récursive, setter de chemin ou boucle de copie qui affecte des clés à un objet ordinaire. Une fusion récursive vulnérable peut suivre __proto__ ou constructor.prototype jusqu’à un prototype partagé ; une copie à un seul niveau peut ne modifier que sa cible. Vérifiez la fonction et l’objet cible avant de décrire l’impact.
Examinez où les clés non fiables sont écrites, pas seulement où le JSON est analysé : fusions profondes, setters de chemin et boucles for...in avec affectation. Refusez __proto__, constructor et prototype lorsque l’utilisateur contrôle les clés ; préférez une forme reconstruite après validation ou un dictionnaire sans prototype.
Questions fréquentes
Un reviver peut supprimer cette clé à l’analyse si l’application n’en a pas besoin. C’est une défense en profondeur, pas un remplacement de la correction des fusions et chemins non sûrs ; constructor.prototype et les entrées non JSON restent à examiner.
L’analyse du corps produit une valeur JavaScript ; elle ne sécurise pas les copies et fusions ultérieures. Vérifiez le parseur exact et les opérations qui suivent. Certains parseurs de query string construisent aussi des objets imbriqués depuis la notation entre crochets : c’est une autre source d’entrée.
Non. La sérialisation lit des valeurs et produit du texte JSON ; elle ne modifie pas de prototype. Le risque peut réapparaître si ce texte est analysé puis transmis à un point non sûr.
Références
- MDN — Pollution de prototype en JavaScript
- MDN — JSON.parse()
- OWASP — Prévention de la pollution de prototype
- CWE-1321 — Modification non contrôlée des attributs du prototype d’objet
- Qu’est-ce que la pollution de prototype ?
- Object.assign() est-il sûr contre la pollution de prototype ?
- JSON.parse() est-il plus sûr que eval() ?