Apakah suntikan __proto__
Inti pati: JSON.parse menganggap __proto__ sebagai kunci biasa; kerosakan berlaku dalam penggabungan rekursif yang menulis ke Object.prototype, jadi setiap objek mewarisi sifat penyerang.

Pokoknya: sekat __proto__, constructor dan prototype sebelum sebarang penggabungan, gabungkan input tidak dipercayai ke dalam Object.create(null), dan tambah penegasan prototaip dalam CI; menampal pustaka sahaja tidak mencukupi.
Dalam satu ayat: kunci tidak dipercayai bernama __proto__ bertemu penggabungan rekursif, dan penggabungan itu menulis ke prototaip.
Satu penghasilan semula
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
Tiada objek perniagaan disentuh. Object.prototype disentuh, 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__ tiba sebagai sifat milik biasa. Bahaya sepenuhnya berada dalam kod yang kemudiannya menggabungkan, mengklon dalam, atau menetapkan mengikut laluan — _.merge, sesetengah pola Object.assign, dan pembantu set(obj, path, value) tulisan tangan yang tidak terbilang.
Kesan
- Pintasan kebenaran:
if (user.isAdmin)menjadi benar tanpa syarat - Kerosakan data: medan tidak dijangka disirikan ke dalam rekod yang disimpan
- Penafian perkhidmatan: objek besar yang dilekatkan pada prototaip menambah memori secara kekal
- Peningkatan: digabungkan dengan enjin templat atau pelaksana arahan, ini telah membawa kepada pelaksanaan kod jauh
Empat pertahanan
Tapis kunci.
const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
Menyekat hanya __proto__ tidak mencukupi: constructor.prototype sampai ke tempat yang sama.
Gabungkan ke dalam objek prototaip null.
const safeTarget = Object.create(null);
Tanpa rantai prototaip, __proto__ hanyalah kunci rentetan.
Gunakan Object.hasOwn dan bukan in. in merujuk rantai prototaip.
Pastikan kebergantungan ditampal. lodash, deepmerge dan json-patch semuanya telah mengeluarkan pembetulan untuk kelas bug ini. Pantau npm audit.
Sahkan ia kekal dibetulkan
Lalukan muatan yang mengandungi __proto__ dan constructor melalui penggabungan anda, kemudian tegaskan:
console.log({}.polluted); // undefined
console.log(Object.prototype.polluted); // undefined
Satu penegasan dalam CI, kos hampir sifar, menangkap regresi daripada pembaikan semula kemudian.
Soalan Lazim
Kunci apa yang harus ditapis oleh penggabungan?
Sekat __proto__, constructor dan prototype bersama-sama. Menapis hanya __proto__ tidak mencukupi, kerana constructor.prototype sampai ke prototaip yang sama.
Mengapa Object.create(null) menghentikan penulisan?
Objek prototaip null tiada rantai prototaip, jadi __proto__ kekal sebagai kunci rentetan biasa. Ia berfungsi sebagai bekas data, tetapi ia tidak menggantikan penapisan kunci atau kemas kini kebergantungan.
Bagaimana saya menguji pencemaran prototaip dalam CI?
Hantar muatan yang mengandungi __proto__ dan constructor melalui penggabungan anda, kemudian tegaskan bahawa {}.polluted dan Object.prototype.polluted ialah undefined. Satu penegasan hampir tidak berharga apa-apa dan menangkap regresi daripada pembaikan semula kemudian.