Refresh: Prototype Chain
Di JavaScript, setiap object mewarisi property dari Object.prototype. Kalau kita membaca property yang tidak ada di object, JS mencari ke prototype-nya, lalu ke prototype dari prototype, dan seterusnya.
const obj = {};
console.log(obj.toString); // ada — diwariskan dari Object.prototype
obj.__proto__ === Object.prototype; // true
Apa itu Prototype Pollution?
Prototype pollution adalah serangan dimana attacker memodifikasi Object.prototype (atau prototype class tertentu), sehingga semua object di aplikasi terpengaruh. Vektor biasanya adalah fungsi deep-merge yang tidak hati-hati memproses key bernama __proto__, constructor, atau prototype.
BURUK: Deep Merge Vulnerable
function deepMerge(target, source) {
for (const key in source) {
if (typeof source[key] === "object") {
target[key] = target[key] || {};
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
// Attacker mengirim JSON:
const payload = JSON.parse('{"__proto__":{"isAdmin":true}}');
deepMerge({}, payload);
// Sekarang SEMUA object punya isAdmin === true
const newUser = {};
console.log(newUser.isAdmin); // true → auth bypass!
BAIK: Cek Key Berbahaya
function safeDeepMerge(target, source) {
for (const key in source) {
if (key === "__proto__" || key === "constructor" || key === "prototype") {
continue; // skip key berbahaya
}
if (typeof source[key] === "object" && source[key] !== null) {
target[key] = target[key] || Object.create(null);
safeDeepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
Lebih Baik: Object.create(null)
// Object tanpa prototype — tidak bisa dicemari
const store = Object.create(null);
store.hello = "world";
console.log(store.__proto__); // undefined
console.log(store.toString); // undefined — tidak ada warisan
Dampak Nyata
- Auth bypass — Polusi
isAdmin,role, atau flag authorization - RCE (Remote Code Execution) — Kalau kode selanjutnya memanggil
evalatauFunctiondengan property yang ter-polusi - DoS — Polusi method internal sehingga aplikasi crash
CVE Terkenal
- lodash < 4.17.12 (CVE-2019-10744) —
_.defaultsDeeprentan prototype pollution - minimist < 1.2.6 (CVE-2021-44906) — argument parser
- jQuery < 3.4.0 —
$.extend(true, ...) - Kibana (CVE-2019-7609) — prototype pollution → RCE di Timelion
Pencegahan
- Object.create(null) — gunakan sebagai container untuk data user
- Map instead of object —
Maptidak punya prototype issues - Deny-list key — block
__proto__,constructor,prototypesaat merge/assign - Object.freeze(Object.prototype) — di early boot, bekukan prototype inti (hati-hati break library)
- Flag Node.js — jalankan dengan
--disable-proto=deleteuntuk menghapus__proto__getter/setter - Schema validation — Zod/Joi tolak object yang tidak match schema
- Update dependencies — banyak pollution fix sudah dirilis
Deteksi di Code Review
// Red flag: deep merge tanpa key check
function merge(a, b) { /* ... source[key] ... */ }
// Red flag: Object.assign recursive tanpa filter
// Red flag: JSON.parse(userInput) langsung ke merge
// Red flag: pakai [] bracket notation dengan key dari user