JSON Toolkit

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.

Lanjutan~5 min bacaanDiterbitkan 2026-10-30Dikemas kini 2026-10-30
Cara pencemaran prototaip berfungsi dan cara menyekatnya

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.

Bacaan lanjut