
Proposition TC39 pour atténuer la pollution des prototypes
Auteurs : Santiago Díaz (Google)
Champion : Shu-yu Guo (Google)
Stade : 1
freeze, seal et preventExtensions
Cette proposition vise à atténuer une vulnérabilité au niveau du langage connue sous le nom de pollution des prototypes, grâce à un mécanisme qui complète les primitives de gel et à un mécanisme pour rendre la plupart des bases de code compatibles avec elle. Elle décrit une fonctionnalité opt-in qui rend les prototypes disponibles uniquement via les API de réflexion. Ainsi, l'instruction obj[key] ne peut plus accéder aux prototypes. Les bases de code compatibles avec cette fonctionnalité sont plus intentionnelles dans leur façon d'utiliser les prototypes.
Les vulnérabilités PP permettent aux attaquants de manipuler des objets qu'ils ne contrôlent pas ou auxquels ils n'ont pas accès à l'exécution. Cette primitive d'« action fantôme à distance » peut être utilisée pour modifier la forme d'autres objets et remplacer leurs propriétés, contaminant ainsi les objets dans l'environnement d'exécution.
Les objets contaminés invalident les hypothèses sous-jacentes du code qui serait autrement sûr/correct et peuvent conduire à une exécution de code arbitraire et à un large éventail d'autres problèmes de sécurité dans les bases de code JS. Les bugs de pollution des prototypes se manifestent souvent dans les applications web, mais affectent également les environnements d'exécution JS non web.
Les propriétés des objets en JS sont modifiables par tout code qui peut y faire référence. En particulier, si de nombreux objets reposent sur une propriété partagée, n'importe lequel d'entre eux peut entraîner des changements sur tous les autres.
Une propriété particulière de la PP est qu'il s'agit d'une attaque par données uniquement, permettant d'atteindre l'exécution de code purement via les données. Par exemple, voyez le code vulnérable suivant et l'exploit correspondant :
// source is attacker-controlled
function merge(target, source) {
for (let key in source) {
if (typeof source[key] === 'object') {
if(target[key] === undefined) {
target[key] = {};
}
target[key] = merge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
// User input comes as a string
const userSuppliedObj = JSON.parse('{"__proto__": {"polluted": true}}');
// Trigger prototype pollution
merge({}, userSuppliedObj);
// Create a brand new object
const newObj = {};
// Has polluted property
console.log(newObj.polluted); // true
Notez que l'exploit parvient à contaminer la création de nouveaux objets sans injecter aucun code étranger.
En raison de cette propriété particulière, les atténuations modernes contre les problèmes d'exécution de code — comme la Content Security Policy ou les Trusted Types — ne suffisent pas à protéger contre la PP, car elles se concentrent sur l'application de la provenance du code.
Notez que les attaques par données uniquement sont pertinentes dans les situations où le code exécuté sur la machine virtuelle est fiable et où l'exécution de code arbitraire a un impact sur la sécurité.
freeze, seal et preventExtensionsLes primitives de gel existantes souffrent de problèmes de conception importants qui rendent leur adoption généralisée peu probable. Elles peuvent être utiles aux utilisateurs experts, mais ne conviennent pas à la majorité des développeurs, qui attendent raisonnablement que les prototypes soient mutables :
Les API de gel souffrent de l'erreur d'override et d'autres incohérences qui introduisent des bugs dans les bases de code existantes, les faisant échouer ou, pire, échouer silencieusement en mode non strict. Une enquête précédente sur l'erreur d'override a conclu que l'erreur d'override se déclenche sur ~10 % des bases de code en mode strict et 20 % en mode non strict. L'enquête a été abandonnée peu après.
Les API de gel confient aux développeurs la lourde responsabilité de savoir quels prototypes doivent être gelés pour maintenir une base de code sécurisée, en supposant que les développeurs sont des experts en sécurité. Ces API décrivent le quoi mais pas le comment de la sécurité. Geler Object n'est certainement pas suffisant, car de nombreux exploits abusent d'Array. Qu'en est-il d'Error, Date, Reflect ou Proxy ? Ou des futurs types natifs ? Les API de gel ne fournissent aucune réponse à ces questions.
Les API de gel supposent un point de gel stable : un moment fixe à l'exécution où les prototypes se sont stabilisés et peuvent être gelés. En pratique, ce point est volatile et change au fil du temps dans les bases de code activement développées. Bien que l'on puisse trouver un tel point dans de nombreuses applications aujourd'hui, l'ajout de nouvelles dépendances, de polyfills, de changements de structure de code et de fonctionnalités avancées comme le hotswapping et les outils de développement font des points de gel une cible mouvante.
Les API de gel ne peuvent pas protéger la chaîne de prototypes complète. En JS, des objets peuvent être ajoutés ou retirés de la chaîne de prototypes à tout moment. Pour protéger la chaîne complète, il faut toujours se souvenir de geler les objets ajoutés à la chaîne, un processus sujet aux erreurs. Une fois retirés de la chaîne, ils ne peuvent plus être dégelés.
En résumé : une fonctionnalité qui n'expose les prototypes qu'aux API de réflexion. Si les prototypes n'étaient pas disponibles via des propriétés comme __proto__ ou prototype, ils ne seraient pas exposés aux problèmes liés aux données uniquement.
Ceci est mieux compris à travers un exemple : l'instruction obj[one][two] = value est vulnérable à la PP via obj.__proto__.polluted. Si l'on supprime la propriété Object.prototype.__proto__, la même instruction n'est plus vulnérable car elle ne peut pas utiliser le seul autre moyen d'atteindre les prototypes, à savoir obj.constructor.prototype.polluted. Notez que le prototype ne peut pas être supprimé.
Cette proposition peut être mise en œuvre en fournissant des API de réflexion et en créant une nouvelle fonctionnalité d'encapsulation opt-in qui supprime les propriétés de prototype. Une description de chaque étape suit.