Apa itu injeksi __proto__
Intinya: JSON.parse memperlakukan __proto__ sebagai key biasa; kerusakan terjadi pada merge rekursif yang menulis ke Object.prototype, sehingga setiap objek mewarisi properti dari penyerang.

Intinya: blokir __proto__, constructor dan prototype sebelum merge apa pun, merge input tak tepercaya ke Object.create(null), dan tambahkan asersi prototype ke CI; menambal library saja tidak cukup.
Dalam satu kalimat: key tak tepercaya bernama __proto__ bertemu merge rekursif, dan merge itu menulis ke prototype.
Reproduksi
function merge(target, source) {
for (const key in source) {
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = target[key] || {};
merge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
merge({}, JSON.parse('{"__proto__": {"isAdmin": true}}'));
console.log({}.isAdmin); // true
Tidak ada objek bisnis yang tersentuh. Object.prototype yang tersentuh, dan setiap objek kini membawa isAdmin.
Mengapa JSON.parse tidak bersalah
const data = JSON.parse('{"__proto__": {"a": 1}}');
Object.getOwnPropertyNames(data); // [ '__proto__' ]
Object.getPrototypeOf(data) === Object.prototype; // true, untouched
__proto__ datang sebagai own property biasa. Bahayanya sepenuhnya berada di kode yang kemudian melakukan merge, deep copy, atau set berdasarkan path — _.merge, sebagian pola Object.assign, dan tak terhitung helper set(obj, path, value) yang ditulis tangan.
Dampak
- Bypass otorisasi:
if (user.isAdmin)menjadi selalu true - Korupsi data: field tak terduga terserialisasi ke catatan yang tersimpan
- Denial of service: objek besar yang menempel pada prototype menumbuhkan memori secara permanen
- Eskalasi: dikombinasikan dengan template engine atau command runner, ini pernah berujung pada remote code execution
Empat pertahanan
Saring key-nya.
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
Memblokir hanya __proto__ tidak cukup: constructor.prototype mencapai tempat yang sama.
Merge ke objek dengan prototype null.
const safeTarget = Object.create(null);
Tanpa rantai prototype, __proto__ hanyalah key string biasa.
Pakai Object.hasOwn alih-alih in. in memeriksa rantai prototype.
Jaga dependensi tetap ter-patch. lodash, deepmerge dan json-patch semuanya telah merilis perbaikan untuk kelas bug ini. Pantau npm audit.
Verifikasi bahwa perbaikannya bertahan
Berikan payload yang mengandung __proto__ dan constructor melalui merge Anda, lalu assert:
console.log({}.polluted); // undefined
console.log(Object.prototype.polluted); // undefined
Satu asersi di CI, biaya hampir nol, menangkap regresi dari refactor berikutnya.
FAQ
Key apa saja yang harus disaring oleh merge?
Blokir __proto__, constructor dan prototype sekaligus. Menyaring hanya __proto__ tidak cukup, karena constructor.prototype mencapai prototype yang sama.
Mengapa Object.create(null) menghentikan penulisan itu?
Objek dengan prototype null tidak punya rantai prototype, sehingga __proto__ tetap menjadi key string biasa. Ia berfungsi sebagai wadah data, tetapi tidak menggantikan penyaringan key atau pembaruan dependensi.
Bagaimana cara menguji prototype pollution di CI?
Kirim payload yang mengandung __proto__ dan constructor melalui merge Anda, lalu assert bahwa {}.polluted dan Object.prototype.polluted bernilai undefined. Satu asersi hampir tanpa biaya dan menangkap regresi dari refactor berikutnya.