flawopen.com/Prototype Pollution/Object.assign()
Apakah Object.assign() aman dari Prototype Pollution?
Tidak sepenuhnya. Object.assign() menyalin properti own yang enumerable dengan menetapkannya ke target. Jika sumber memiliki key own enumerable __proto__, assignment dapat memanggil setter warisan dan mengubah prototipe objek target. Satu assign dangkal tidak dengan sendirinya mengubah Object.prototype bersama; kode merge rekursif dapat menimbulkan dampak lebih luas.
// A JSON own "__proto__" key reaches the inherited setter const source = JSON.parse('{"__proto__":{"isAdmin":true}}'); const target = Object.assign({}, source); // target's prototype changes; Object.prototype does not. // Recursive merge can reach Object.prototype function deepAssign(target, source) { for (const key of Object.keys(source)) { if (source[key] && typeof source[key] === 'object') { target[key] = target[key] || {}; deepAssign(target[key], source[key]); // With key === '__proto__', target[key] can be // Object.prototype; recursion then writes into it. } else { Object.assign(target, { [key]: source[key] }); } } }
// Spread creates an own data property; it does not invoke the setter. const shallow = { ...userInput }; // Reject dangerous keys at every depth and use own data properties. const blocked = new Set(['__proto__', 'constructor', 'prototype']); function isRecord(value) { if (value === null || typeof value !== 'object' || Array.isArray(value)) return false; const proto = Object.getPrototypeOf(value); return proto === Object.prototype || proto === null; } function copySafe(value) { if (Array.isArray(value)) return value.map(copySafe); return isRecord(value) ? mergeSafe(value) : value; } function mergeSafe(source, target = Object.create(null)) { if (!isRecord(source)) throw new TypeError('Expected a plain object'); for (const key of Object.keys(source)) { if (blocked.has(key)) continue; const value = source[key]; const current = Object.hasOwn(target, key) ? target[key] : undefined; const next = isRecord(value) ? mergeSafe(value, isRecord(current) ? current : Object.create(null)) : copySafe(value); Object.defineProperty(target, key, { value: next, enumerable: true, writable: true, configurable: true }); } return target; }
Cara Object.assign() menangani key __proto__
Object.assign() menyalin setiap properti own enumerable dari sumber melalui perilaku assignment normal pada target. Jika sumber hasil parse JSON memiliki properti own __proto__ dan target mewarisi setter lama, setter itu mengubah [[Prototype]] target. Dalam kasus ini, properti own literal bernama __proto__ tidak dibuat.
Object spread ({ ...source }) membuat properti data pada hasil; key yang sama menjadi properti data own dan tidak memanggil setter warisan. Ini berguna untuk salinan dangkal, tetapi bukan perlindungan umum bagi merge rekursif berikutnya.
Bagaimana merge rekursif dapat mencemari prototipe bersama
Prototype Pollution terjadi ketika kode rekursif yang tidak aman mengikuti key yang dikendalikan penyerang lalu menulis ke prototipe bersama. Pola umum membaca target[key] untuk mencari objek bertingkat. Jika key adalah __proto__, pembacaan itu dapat menghasilkan Object.prototype; rekursi berikutnya lalu berdampak pada objek biasa secara global. Jalur seperti constructor.prototype juga perlu diperiksa.
Saat review, cari operasi yang membaca atau menulis target dengan key yang dikendalikan penyerang, periksa apakah setter terpanggil, dan pastikan setiap tingkat rekursi menolak key berbahaya.
Tanya jawab
{ ...obj }) sama amannya?Ya untuk salinan dangkal: spread membuat properti data own dan tidak memanggil setter warisan. Sebaliknya, Object.assign() dapat mengubah prototipe target jika menerima key sumber enumerable __proto__. Tidak satu pun operasi dangkal itu sendiri mengubah Object.prototype bersama.
structuredClone() membuat salinan terpisah dan tidak menerapkan rantai prototipe sumber. Key data own __proto__ tetap menjadi data di salinan; merge tidak aman berikutnya masih dapat menafsirkannya secara khusus.
Gunakan pustaka merge yang dipelihara dan terbaru; periksa cara menangani __proto__, constructor, dan prototype di setiap tingkat. Sejumlah utilitas merge dan object-path populer pernah memiliki advisory Prototype Pollution; hanya versi yang sudah diperbaiki yang memuat patch.