صفحة إعدادات الغربان تدمج JSON الذي ترسله في كائن الإعدادات بدمج متكرر دون حماية فإذا حمل مدخلك المفتاح الخاص __proto__ لوثت النموذج الأولي المشترك فتصير كل لوحة جديدة ترى نفسها مديرا
صفحة إعدادات تدمج JSON في كائن الإعدادات وزر يفتح لوحة جديدة تقرأ isAdmin من كائن جديد الكائن الجديد لا يملك isAdmin فيرثه من النموذج الأولي فإذا لوثته صارت اللوحة لوحة مدير
لماذا نجح الحل
الدمج المتكرر كتب في النموذج الأولي المشترك عبر المفتاح __proto__ فكل كائن جديد يرث الخاصية الملوثة الحل منع مفاتيح __proto__ و constructor و prototype واستخدام خرائط Map أو كائنات بلا نموذج أولي
السبب الجذري
دمج متكرر يكتب مفتاح __proto__ في النموذج الأولي الجذر الثقة في مفاتيح كائن المستخدم دون منع المفاتيح الخطرة
كيف يصلح الكود
امنع __proto__ و constructor و prototype في الدمج واستخدم Map أو كائنا بلا نموذج أولي
الكود المصاب
function merge(target, source) {
for (const key in source) {
if (typeof source[key] === 'object') merge(target[key] ||= {}, source[key]);
else target[key] = source[key]; // __proto__ flows straight through
}
}
الكود الآمن
const DANGEROUS = new Set(['__proto__', 'constructor', 'prototype']);
function merge(target, source) {
for (const key of Object.keys(source)) {
if (DANGEROUS.has(key)) continue; // never merge prototype-reaching keys
if (isPlainObject(source[key])) merge(target[key] ||= {}, source[key]);
else target[key] = source[key];
}
}
الأثر في الواقع
تلويث النموذج الأولي يغير سلوك التطبيق كله ويقود لتجاوز صلاحيات أو تنفيذ شيفرة في بيئات معينة
كيف يكتشفها المدافعون
المدافع يرصد مفاتيح __proto__ و constructor في المدخلات ويختبر أن خصائص الكائنات لم تتغير بعد الدمج