FlawOpen

flawopen.com/Vulnérabilités/Pollution de prototypes JavaScript

Pollution de prototypes JavaScript

Gravité élevée CWE-1321 Chaîne d’approvisionnement et runtime
En clair — Un plan partagé

Imaginez un atelier où chaque nouvel appareil consulte un règlement partagé lorsque ses propres réglages ne répondent pas à une question. Si un préposé y inscrit une règle contrôlée par un attaquant, d’autres appareils peuvent la prendre pour une valeur par défaut. La pollution de prototypes est l’équivalent logiciel : des écritures dynamiques non sûres peuvent modifier le prototype d’un objet, parfois Object.prototype. L’effet n’est pas forcément global; l’impact suppose que le code fasse ensuite confiance à la propriété héritée.

Cible :JavaScript côté serveur ou navigateur qui fusionne des clés non fiables ou écrit des chemins de propriétés contrôlés par l’utilisateur
Vecteur :Clés non fiables comme __proto__, ou chemin constructor.prototype menant à une écriture non sûre
Impact :Dépend d’un gadget accessible : changement de logique, déni de service, XSS ou, dans certains cas côté serveur, exécution de code; rien n’est automatique
Correction :Validez par schéma ou liste d’autorisation; utilisez Map ou des dictionnaires sans prototype pour les clés non fiables

Concepts clés et termes du sous-système

Object.prototype
Prototype partagé hérité par les objets ordinaires. Sa pollution peut affecter de nombreux objets, mais pas tous les objets JavaScript ni tous les realms.
__proto__
Accesseur historique des objets ordinaires qui lit ou définit leur prototype. Dans du JSON, cette graphie est un nom de propriété ordinaire jusqu’à son traitement dangereux par du code ultérieur.
Fusion récursive profonde
Fonction qui parcourt des clés imbriquées et les écrit dans un autre objet. Elle devient dangereuse si des clés non fiables sélectionnent des propriétés liées aux prototypes ou des chemins non sûrs.
CWE-1321
Modification insuffisamment contrôlée des attributs de prototype d’objet. La faiblesse est la mutation non contrôlée; l’impact dépend ensuite du comportement accessible de l’application.

Déroulement de l’attaque, étape par étape

Étape 1

Entrée de propriétés non fiables

Un endpoint accepte des clés d’objet contrôlées par l’attaquant. Analyser du JSON contenant une propriété propre __proto__ ne modifie pas à lui seul un prototype.

Étape 2

Écriture dynamique non sûre

Une fusion récursive ou un setter de chemin suit ces clés et effectue des écritures dynamiques sans valider chaque segment.

Étape 3

Modification du prototype cible ou partagé

Selon la cible et l’ordre des écritures, l’opération modifie le prototype d’une cible ou atteint un prototype partagé comme Object.prototype. Ce sont deux résultats distincts.

Étape 4

Un gadget détermine l’impact

Un gadget ultérieur lit la valeur héritée dans une opération sensible à la sécurité. Le résultat dépend de ce gadget; ne présumez ni contournement d’authentification ni exécution de code.

Mécanisme et cause racine

La recherche de propriétés JavaScript suit la chaîne de prototypes. Une fusion vulnérable ou un setter de chemin peut laisser des clés non fiables comme __proto__ ou constructor.prototype diriger une écriture vers un prototype. Un champ propre __proto__ créé par JSON.parse n’est pas une pollution en soi; un autre code doit l’interpréter par une affectation non sûre. Modifier le prototype d’une cible affecte cette cible; modifier Object.prototype affecte les objets ordinaires qui en héritent. Un gadget distinct doit utiliser la propriété héritée pour qu’il y ait un impact de sécurité.

utils.js (Fusion récursive vulnérable)Vulnérable
// VULNÉRABLE : des clés contrôlées par l’attaquant atteignent les écritures récursives
function merge(target, source) {
    for (const key in source) {
        const value = source[key];
        if (value && typeof value === 'object') {
            if (!target[key]) target[key] = {};
            merge(target[key], value);
        } else {
            target[key] = value;
        }
    }
    return target;
}
// JSON.parse crée une propriété propre __proto__ ; la fusion non sûre est le risque.
const input = JSON.parse('{"__proto__": {"isAdmin": true}}');
merge({}, input);
utils.js (Analyse du profil par liste autorisée)Sécurisé
// SÉCURISÉ : ne copier que les champs acceptés par cet endpoint
const PROFILE_KEYS = new Set(['displayName', 'theme']);

function parseProfile(input) {
    if (!input || typeof input !== 'object' || Array.isArray(input)) {
        throw new TypeError('profile must be an object');
    }

    const profile = Object.create(null);
    for (const key of Object.keys(input)) {
        if (!PROFILE_KEYS.has(key)) continue;
        const value = input[key];
        if (typeof value !== 'string' || value.length > 80) {
            throw new TypeError('invalid profile field');
        }
        profile[key] = value;
    }
    return profile;
}

Séquence d’attaque et d’exploitation

Mesures défensives et règles de prévention

Questions fréquentes

JSON.parse() pollue-t-il le prototype à lui seul ?

Non. JSON.parse() crée une propriété propre appelée __proto__ et n’appelle pas le setter hérité. Le risque apparaît si une fusion ou affectation ultérieure appelle ce setter. Voir JSON.parse() provoque-t-il une Prototype Pollution ?

Object.freeze(Object.prototype) corrige-t-il le problème ?

Cela peut empêcher les écritures sur Object.prototype lui-même lorsqu’il est gelé, mais pas un setter qui modifie le prototype d’un objet individuel, ni tous les autres prototypes. Cela peut aussi casser des bibliothèques qui étendent les objets intégrés. À utiliser en défense en profondeur, pas comme correctif du traitement des données.

TypeScript est-il protégé ?

Non. Les types disparaissent à l’exécution. Une valeur typée Record<string, string> peut encore contenir une clé __proto__ ; validez les clés non fiables et construisez les objets sans danger.

Consultez les sujets et analyses de sécurité associés :Répertoire complet de sécurité →