flawopen.com/Prototype pollution/JSON.parse()
Apakah JSON.parse() menyebabkan prototype pollution?
Tidak. JSON.parse() membuat properti data milik sendiri bernama __proto__; proses parsing tidak memanggil setter warisan atau mengubah prototipe. Risiko muncul kemudian jika kode menyalin kunci itu melalui setter, menggabungkan kunci tak tepercaya secara rekursif, atau menulis ke jalur objek yang dikendalikan penyerang.
// JSON.parse membuat data; tidak mencemari prototipe. const input = '{"__proto__":{"isAdmin":true}}'; const parsed = JSON.parse(input); // Object.assign menggunakan Set pada target. Di sini hanya // prototipe objek demo lokal ini yang berubah. const copied = Object.assign({}, parsed); Object.getPrototypeOf(copied).isAdmin; // true Object.prototype.isAdmin; // undefined
// Parse lalu bentuk hanya bidang yang diizinkan aplikasi. const input = '{"theme":"dark","locale":"en","__proto__":{"isAdmin":true}}'; const parsed = JSON.parse(input); const config = { theme: parsed.theme, locale: parsed.locale, }; // Untuk peta berkunci tak tepercaya, hindari setter warisan. const options = Object.assign(Object.create(null), parsed);
Mengapa JSON.parse() bukan titik pencemaran
Kunci JSON dan setter prototipe pada object literal adalah operasi berbeda. Parsing JSON mendefinisikan properti milik sendiri pada nilai baru; proses itu tidak menjalankan obj.__proto__ = value. Operasi berikutnya dapat berperilaku lain: Object.assign(target, source) melakukan assignment ke target, sehingga kunci milik sendiri __proto__ dapat memanggil setter warisan target. Ini mengubah prototipe objek target; tindakan ini saja tidak mengubah Object.prototype.
Setelah const o = JSON.parse('{"__proto__":{"a":1}}'), Object.hasOwn(o, "__proto__") bernilai true dan Object.prototype.a tetap undefined. Penyalinan berikutnya ke target biasa dapat mengubah prototipe target tersebut. Object spread membuat properti milik sendiri tanpa memanggil setter, tetapi bukan perlindungan terhadap merge rekursif atau penulisan jalur yang tidak aman.
Di mana risiko sebenarnya muncul
JSON yang telah diparse adalah data tak tepercaya, bukan prototipe yang tercemari. Risikonya bergantung pada sink penulisan berikutnya: merge rekursif, setter jalur, atau loop penyalinan yang mengassign kunci ke objek biasa. Merge rekursif yang rentan dapat menelusuri __proto__ atau constructor.prototype hingga prototipe bersama; penyalinan satu tingkat mungkin hanya mengubah target. Telusuri helper dan objek target yang sebenarnya sebelum menyimpulkan dampak.
Tinjau tempat kunci tak tepercaya ditulis, bukan hanya tempat JSON diparse: helper deep-merge, setter jalur objek, dan loop for...in yang melakukan assignment. Tolak __proto__, constructor, dan prototype jika pengguna mengendalikan kunci; lebih baik bangun ulang bentuk tervalidasi atau gunakan kamus tanpa prototipe.
Pertanyaan umum
Reviver dapat membuang kunci itu saat parsing jika aplikasi tidak membutuhkannya. Ini pertahanan berlapis, bukan pengganti perbaikan merge dan penulisan jalur yang tidak aman; constructor.prototype dan sumber non-JSON juga perlu diperiksa.
Parser body menghasilkan nilai JavaScript; parser tidak membuat penyalinan atau merge setelahnya aman. Tinjau parser yang benar-benar digunakan dan operasi lanjutan. Beberapa parser query string juga membangun objek bertingkat dari sintaks kurung siku, jalur input yang berbeda.
Tidak. Serialisasi membaca nilai dan menghasilkan teks JSON; ia tidak mengubah prototipe. Risiko dapat muncul kembali jika teks itu diparse lalu diteruskan ke sink yang tidak aman.