JSON Toolkit

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.

Lanjutan±5 menit bacaDiterbitkan 2026-10-30Diperbarui 2026-10-30
Cara kerja prototype pollution dan cara memblokirnya

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.

Bacaan lanjutan