تخطي للذهاب إلى المحتوى

تلويث النموذج الأولي في JavaScript: كيف يعمل وكيف تمنعه

كيف تجعل دالة دمج واحدة غير آمنة كل كائن في تطبيقك مديراً، والممارسات التي توقف ذلك
4 يناير 2026 بواسطة
تلويث النموذج الأولي في JavaScript: كيف يعمل وكيف تمنعه
Ibrahim Zakaria


أمان JavaScript في الواقع العملي

تشغّل JavaScript المواقع ولوحات المعلومات وتطبيقات الهاتف وحصة كبيرة من الأنظمة الخلفية، وإن كنت تبني البرمجيات اليوم فأنت على الأرجح تنشر كود JavaScript.

يتعلّم معظم المطوّرين كيف يجعلون JavaScript تعمل، وقلّة منهم تتعلّم كيف تجعلها آمنة. فالمقرّرات الجامعية في بيروت ومختلف أنحاء لبنان تميل إلى التركيز على قواعد اللغة والمنطق الأساسي والمشاريع الصغيرة، ونادراً ما تدخل مخاطر أمان JavaScript الفعلية في المنهج.

ومن الأمثلة الجيدة على ذلك تلويث النموذج الأولي (prototype pollution)، وهي ثغرة لم يسمع بها كثير من المطوّرين، ومع ذلك قد تعطّل تطبيقاً بالكامل.

يشرح هذا المقال تلويث النموذج الأولي بلغة بسيطة، ويبيّن لماذا هو خطير، ويعدّد ممارسات أمان JavaScript التي تحمي تطبيقك منه.

النماذج الأولية في JavaScript بعبارات بسيطة

تعمل JavaScript بطريقة مختلفة عن كثير من لغات البرمجة الأخرى.
فبدل الوراثة التقليدية، تستخدم JavaScript النماذج الأولية (prototypes).

يمكن لكل كائن في JavaScript أن يرث خصائص من كائن آخر يُسمّى النموذج الأولي له. وحين تقرأ خاصية:

  1. تبحث JavaScript في الكائن نفسه
  2. إن لم تكن الخاصية موجودة فيه، تبحث في النموذج الأولي
  3. ويستمر ذلك صعوداً في السلسلة حتى تصل إلى Object.prototype

هذا التصميم مرن، لكنه يحمل خطراً.
فإن تمكّن أحدهم من تغيير Object.prototype، يصل التغيير إلى كل كائن في تطبيقك.
وهنا يظهر تلويث النموذج الأولي.

ما هو تلويث النموذج الأولي؟

يحدث تلويث النموذج الأولي حين يتمكّن مهاجم من إضافة خصائص إلى Object.prototype، وغالباً عبر بيانات يقبلها تطبيقك من الخارج.

عندما يحدث ذلك:

  • قد يرث كل كائن في تطبيقك القيم التي وضعها المهاجم
  • قد تفشل فحوص الأمان من دون أي خطأ
  • قد يُتجاوز منطق العمل بصمت

ولأن شيئاً لا يتعطّل، يصعب اكتشاف هذا النوع من الثغرات، ويزداد صعوبةً تتبّعه وإصلاحه.

مثال واقعي: دمج غير آمن للكائنات

تدمج كثير من تطبيقات JavaScript مدخلات المستخدم مع كائنات الإعدادات الافتراضية.

قد تبدو دالة دمج تكرارية (recursive) بسيطة هكذا:

function merge(target, source) {
  for (let key in source) {
    if (typeof target[key] === 'object' && target[key] !== null) {
      merge(target[key], source[key]);
    } else {
      target[key] = source[key];
    }
  }
}

تخيّل الآن أن مهاجماً يرسل جسم JSON هذا:

{
  "__proto__": {
    "isAdmin": true
  }
}

تحوّل JSON.parse المفتاح "__proto__" إلى مفتاح عادي، فتمرّ عليه الحلقة. لكن target["__proto__"] هو Object.prototype نفسه، فيكتب الدمج isAdmin في النموذج الأولي الذي تتشاركه كل الكائنات.

ومنذ تلك اللحظة يصبح لدى كل كائن في العملية:

({}).isAdmin === true

لماذا يشكّل ذلك خطراً أمنياً جدياً

حين يتلوّث النموذج الأولي قد يكون الأثر بالغاً:

  • تجاوز المصادقة
    إن كان كودك يتحقق من الصلاحيات بـ if (user.isAdmin)، ولم يكن للكائن user خاصية isAdmin خاصة به، فقد يحصل المهاجمون على صلاحيات المدير.
  • سلوك غير متوقع
    ينكسر المنطق بطرق يصعب تتبّعها.
  • أعطال أو حجب للخدمة
    قد تُستبدل خصائص يعتمد عليها كودك.

وقد اكتُشفت هذه الهجمات في أنظمة إنتاج حقيقية وفي مكتبات واسعة الاستخدام.

ممارسات أمان JavaScript التي تمنع تلويث النموذج الأولي

1. لا تثق بمدخلات المستخدم أبداً

تحقّق من البيانات الواردة وفق مخطّط (schema)، وارفض الكائنات التي تحتوي مفاتيح خطرة مثل __proto__ أو constructor أو prototype. وافحص مفاتيح الكائن الخاصة به، لأن فحصاً مثل obj.__proto__ ينظر إلى النموذج الأولي الموروث ويكون صحيحاً لكل كائن عادي.

const BLOCKED = new Set(["__proto__", "constructor", "prototype"]);

function sanitize(obj) {
  for (const key of Object.keys(obj)) {
    if (BLOCKED.has(key)) {
      throw new Error("Prototype pollution attempt: " + key);
    }
    const value = obj[key];
    if (typeof value === "object" && value !== null) {
      sanitize(value);
    }
  }
}

والقاعدة نفسها مكانها دالة الدمج لديك، فتخطَّ المفاتيح المحظورة ولا تتّبع إلا المفاتيح الخاصة بالكائن نفسه (Object.hasOwn(source, key)).

2. استخدم Object.create(null) أو Map للقواميس

الكائنات المُنشأة بـ Object.create(null) لا ترث من Object.prototype.

const safeObject = Object.create(null);
safeObject.key = "value";

وللبيانات التي تُفهرس بنصوص يقدّمها المستخدم، يتجنّب Map المشكلة كلياً، وكلتا الطريقتين تُزيلان فئات كاملة من الهجمات.

3. احذر عند استخدام المكتبات

شحنت مكتبات شائعة أيضاً أخطاء تلويث للنموذج الأولي، وقد رُقّعت أدوات الدمج العميق مثل lodash.merge لهذا السبب تحديداً.

إن كنت تستخدمها:

  • حدّثها باستمرار وتابع التنبيهات الأمنية (يساعدك npm audit في ذلك)
  • نقِّ المدخلات قبل الدمج
  • اتّبع إرشادات الأمان الموثّقة للمكتبة

4. جمّد النماذج الأولية (بحذر)

Object.freeze(Object.prototype);

يمنع هذا أي تغيير على النموذج الأولي، لكنه قد يعطّل مكتبات توسّعه، لذلك اختبر جيداً قبل تفعيله في بيئة الإنتاج. وفي Node.js يُزيل الخيار --disable-proto=delete الموصّل __proto__ كلياً.

لماذا لا يصادف كثير من المطوّرين هذا في الجامعة

تغطي البرامج الأكاديمية عادةً قواعد اللغة والامتحانات والمشاريع الصغيرة، أما برمجيات الإنتاج فتحتاج أيضاً إلى تفكير أمني، وفهم لطريقة عمل الهجمات، وعادات برمجة دفاعية، والممارسات التي تعلّمها القطاع بالطريقة الصعبة. ويقع تلويث النموذج الأولي في هذه الفجوة، ومعه كثير من المشكلات التي نجدها حين نراجع قواعد كود حقيقية.

فحص كودك أنت

تلويث النموذج الأولي مثال واحد من أمثلة كثيرة، والكود المولَّد بأدوات الذكاء الاصطناعي يجعل هذه المشكلات أكثر شيوعاً، لأن دالة دمج أو أداة تحميل إعدادات هي بالضبط نوع الكود الذي يكتبه المساعد بسرعة ولا يراجعه أحد. أما خدمة تقييم الكود المكتوب بالذكاء الاصطناعي لدينا فتفحص التطبيق، أياً كان من كتبه أو ما كتبه، من حيث الأمان وأفضل الممارسات وقابلية الصيانة والجاهزية للإنتاج، وتوثّق كل ملاحظة مع خطوات إعادة إنتاجها. وإن كنت تبني منتجاً جديداً، فإن فريق البرمجيات المخصّصة لدينا يكتب الكود وهذه الفحوص قائمة منذ البداية.

إن لم تكن متأكداً مما إذا كان تطبيقك مكشوفاً، فاحجز مكالمة مجانية مدتها 30 دقيقة وحدّثنا عن التقنيات التي تستخدمها.

تلويث النموذج الأولي في JavaScript: كيف يعمل وكيف تمنعه
Ibrahim Zakaria 4 يناير 2026
شارك هذا المنشور
علامات التصنيف
راسلنا على واتسابوكيل ذكي يردّ على مدار الساعة
احجز مكالمة مجانية