JSON Toolkit

تزریق __proto__ چیست

جمع‌بندی: JSON.parse با __proto__ به‌عنوان کلید معمولی برخورد می‌کند؛ آسیب در ادغام بازگشتی‌ای رخ می‌دهد که در Object.prototype می‌نویسد، پس هر شیء ویژگی‌های مهاجم را به ارث می‌برد.

پیشرفتهحدود 5 دقیقه مطالعهانتشار 2026-10-30به‌روزرسانی 2026-10-30
آلودگی prototype چگونه کار می‌کند و چگونه مسدودش کنیم

جمع‌بندی: پیش از هر ادغام __proto__، constructor و prototype را مسدود کنید، ورودی نامعتمد را در Object.create(null) ادغام کنید و یک اعلان prototype به CI اضافه کنید؛ فقط وصله کردن کتابخانه‌ها کافی نیست.

در یک جمله: کلید نامعتمد به نام __proto__ با یک ادغام بازگشتی برخورد می‌کند و ادغام در prototype می‌نویسد.

یک بازتولید

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

هیچ شیء کسب‌وکاری دست نخورد. Object.prototype دست خورد، و هر شیء اکنون isAdmin دارد.

چرا JSON.parse مقصر نیست

const data = JSON.parse('{"__proto__": {"a": 1}}');
Object.getOwnPropertyNames(data);                  // [ '__proto__' ]
Object.getPrototypeOf(data) === Object.prototype;  // true, untouched

__proto__ به‌عنوان یک ویژگی خودی معمولی می‌رسد. خطر کاملاً در کدی است که بعداً ادغام، شبیه‌سازی عمیق یا تنظیم بر اساس مسیر می‌کند — _.merge، برخی الگوهای Object.assign و بی‌شمار کمک‌تابع دست‌نویس set(obj, path, value).

اثر

  • دور زدن مجوز: if (user.isAdmin) بی‌قید و شرط درست می‌شود
  • خرابی داده: فیلدهای غیرمنتظره در رکوردهای ذخیره‌شده سریالایز می‌شوند
  • منع سرویس: اشیای بزرگ متصل به prototype حافظه را به‌طور دائم رشد می‌دهند
  • تشدید: در ترکیب با موتورهای قالب یا اجراکننده‌های فرمان، به اجرای کد از راه دور انجامیده است

چهار دفاع

کلیدها را فیلتر کنید.

const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);

مسدود کردن فقط __proto__ کافی نیست: constructor.prototype به همان جا می‌رسد.

در شیء بدون prototype ادغام کنید.

const safeTarget = Object.create(null);

بدون زنجیره prototype، __proto__ فقط یک کلید رشته‌ای است.

از Object.hasOwn به‌جای in استفاده کنید. in زنجیره prototype را مشورت می‌کند.

وابستگی‌ها را وصله‌شده نگه دارید. lodash، deepmerge و json-patch همه برای این دسته باگ اصلاحیه منتشر کرده‌اند. npm audit را زیر نظر بگیرید.

تأیید کنید که اصلاح می‌ماند

payloadی شامل __proto__ و constructor را از ادغامتان عبور دهید، سپس اعلان کنید:

console.log({}.polluted);                 // undefined
console.log(Object.prototype.polluted);   // undefined

یک اعلان در CI، هزینه تقریباً صفر، رگرسیون‌های بازسازی‌های بعدی را می‌گیرد.

پرسش‌های متداول

یک ادغام باید چه کلیدهایی را فیلتر کند؟

__proto__، constructor و prototype را با هم مسدود کنید. فیلتر کردن فقط __proto__ کافی نیست، چون constructor.prototype به همان prototype می‌رسد.

چرا Object.create(null) نوشتن را متوقف می‌کند؟

شیء بدون prototype زنجیره prototype ندارد، پس __proto__ کلید رشته‌ای معمولی می‌ماند. به‌عنوان ظرف داده کار می‌کند، اما جای فیلتر کلید یا به‌روزرسانی وابستگی را نمی‌گیرد.

چگونه در CI برای آلودگی prototype آزمون بنویسم؟

payloadی شامل __proto__ و constructor را از ادغامتان عبور دهید، سپس اعلان کنید که {}.polluted و Object.prototype.polluted مقدار undefined دارند. یک اعلان تقریباً هیچ هزینه‌ای ندارد و رگرسیون‌های بازسازی‌های بعدی را می‌گیرد.

مطالعه بیشتر