استخدام eval في إضافات Chrome

يفرض نظام إضافات Chrome سياسة أمان المحتوى (CSP) تلقائية صارمة نوعًا ما. تكون قيود السياسة واضحة ومباشرة: يجب نقل النص البرمجي خارج السطر إلى ملفات JavaScript منفصلة، ويجب تحويل معالِجات الأحداث المضمّنة لاستخدام addEventListener، وإيقاف سياسة eval(). تتمتع تطبيقات Chrome بسياسة أكثر صرامة، ونحن سعداء جدًا بخصائص الأمان التي توفرها هذه السياسات.

وندرك أنّ مجموعة متنوعة من المكتبات تستخدم التركيبات الشبيهة eval() وeval، مثل new Function()، لتحسين الأداء وتسهيل التعبير. تكون مكتبات النماذج عرضة بشكل خاص لهذا النوع من التنفيذ. على الرغم من أنّ بعضها (مثل Angular.js) يتوافق مع سياسة أمان المحتوى (CSP) بشكل تلقائي، لم يتم بعد تحديث العديد من أُطر العمل الشائعة باستخدام آلية متوافقة مع المحتوى الذي لا يستخدم فيه الإضافات eval. وبالتالي، أثبتت إزالة إمكانية استخدام هذه الوظيفة أنّ المشكلة أكثر مما هو متوقع بالنسبة إلى المطوّرين.

يقدم هذا المستند وضع الحماية كآلية آمنة لتضمين هذه المكتبات في مشاريعك بدون المساس بالأمان. للإيجاز، سنستخدم مصطلح الإضافات، ولكن المفهوم ينطبق بشكلٍ متساوٍ على التطبيقات.

ما أهمية وضع الحماية؟

eval هو أمر خطير في إحدى الإضافات، لأنّ الرمز الذي ينفّذه يمكنه الوصول إلى كل المحتوى في البيئة ذات الإذن العالي الخاصة بالإضافة. تتوفّر مجموعة كبيرة من واجهات برمجة التطبيقات chrome.* الفعّالة التي قد تؤثر بشكل كبير في أمان المستخدم وخصوصيته، ولا داعي للقلق بشأن استخراج البيانات البسيط. الحل المعروض هو وضع حماية يمكن لـ eval من خلاله تنفيذ رمز بدون الوصول إلى بيانات الإضافة أو واجهات برمجة التطبيقات العالية القيمة للإضافة. ما مِن بيانات أو واجهات برمجة تطبيقات ولا مشكلة.

يتم تحقيق ذلك من خلال إدراج ملفات HTML محددة داخل حزمة الإضافات باعتبارها في وضع الحماية. عند تحميل صفحة في وضع الحماية، سيتم نقلها إلى مصدر فريد، ولن يتم منع الوصول إلى واجهات برمجة تطبيقات chrome.*. إذا قمنا بتحميل صفحة وضع الحماية هذه في إضافتنا عبر iframe، يمكننا تمرير الرسائل لها، والسماح لها بالتعامل مع هذه الرسائل بطريقة ما، والانتظار إلى أن تعيدنا النتيجة. توفّر لنا آلية المراسلة البسيطة هذه كل ما نحتاج إليه لتضمين الرمز المستند إلى eval بأمان في سير عمل الإضافة.

إنشاء وضع الحماية واستخدامه

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

إدراج الملفات في البيان

إنّ كل ملف يجب تشغيله داخل وضع الحماية يجب إدراجه في بيان الإضافة من خلال إضافة السمة sandbox. إنّ هذه الخطوة مهمة ويسهل نسيانها، لذا يُرجى التحقق جيدًا من أن الملف المحمي في وضع الحماية مُدرج في البيان. في هذا النموذج، نضع في وضع الحماية للملف باسم "sandbox.html". يبدو إدخال البيان على النحو التالي:

{
  ...,
  "sandbox": {
     "pages": ["sandbox.html"]
  },
  ...
}

تحميل الملف المحمي في وضع الحماية

لكي نفعل شيئًا مثيرًا للاهتمام مع الملف المحمي في وضع الحماية، نحتاج إلى تحميله في سياق يمكن معالجته من خلال رمز الإضافة. في هذا المثال، تم تحميل sandbox.html في صفحة الحدث التابعة للإضافة (eventpage.html) من خلال iframe. يحتوي eventpage.js على رمز يُرسل رسالة إلى وضع الحماية عند النقر على إجراء المتصفح من خلال العثور على iframe في الصفحة، وتنفيذ طريقة postMessage على contentWindow. والرسالة هي عنصر يحتوي على خاصيتَين: context وcommand. سوف نتعمق في كليهما في لحظة.

chrome.browserAction.onClicked.addListener(function() {
 var iframe = document.getElementById('theFrame');
 var message = {
   command: 'render',
   context: {thing: 'world'}
 };
 iframe.contentWindow.postMessage(message, '*');
});
للحصول على معلومات عامة عن واجهة برمجة التطبيقات postMessage API، يمكنك الاطّلاع على مستندات postMessage حول MDN . إنه كامل جدًا وتستحق القراءة. على وجه الخصوص، لاحظ أنه لا يمكن تمرير البيانات ذهابًا وإيابًا إلا إذا كانت قابلة للتسلسل. الدوال، على سبيل المثال، ليس كذلك.

تنفيذ إجراءات خطيرة

عند تحميل sandbox.html، يتم تحميل مكتبة "الأشرطة المقودة" وتنشئ نموذجًا مضمّنًا وتجمعه بالطريقة التي تقترحها "الأشرطة المقودة":

<script src="handlebars-1.0.0.beta.6.js"></script>
<script id="hello-world-template" type="text/x-handlebars-template">
  <div class="entry">
    <h1>Hello, !</h1>
  </div>
</script>
<script>
  var templates = [];
  var source = document.getElementById('hello-world-template').innerHTML;
  templates['hello'] = Handlebars.compile(source);
</script>

هذا لا يفشل! مع أنّ Handlebars.compile يستخدم new Function في نهاية المطاف، تعمل الأمور كما هو متوقع تمامًا، وينتهي بنا الأمر باستخدام نموذج مجمّع في templates['hello'].

تمرير النتيجة مرة أخرى

سنجعل هذا النموذج متاحًا للاستخدام عن طريق إعداد أداة معالجة الرسائل التي تقبل الأوامر من صفحة الحدث. سنستخدم command التي تم تمريرها لتحديد الإجراءات التي يجب اتّخاذها (يمكنك أن تتخيل إجراء أكثر من مجرد العرض، وربما إنشاء نماذج؟ ربما يديرونها بطريقة ما؟)، وسيتم تمرير context إلى النموذج مباشرةً للعرض. سيتم تمرير رمز HTML المعروض مرة أخرى إلى صفحة الحدث بحيث يمكن للإضافة الاستفادة منها في وقت لاحق:

<script>
  window.addEventListener('message', function(event) {
    var command = event.data.command;
    var name = event.data.name || 'hello';
    switch(command) {
      case 'render':
        event.source.postMessage({
          name: name,
          html: templates[name](event.data.context)
        }, event.origin);
        break;

      // case 'somethingElse':
      //   ...
    }
  });
</script>

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

تجعل هذه الآلية وضع النماذج مباشرة، لكنها بالطبع لا تقتصر على النماذج. يمكن وضع أي رمز برمجي لا يعمل وفقًا لسياسة أمان محتوى صارمة في وضع الحماية، وفي الواقع، من المفيد غالبًا وضع حماية لمكونات الإضافات التي سيتم تشغيلها بشكل صحيح من أجل حصر كل جزء من برنامجك على أصغر مجموعة من الامتيازات اللازمة لتنفيذه بشكل صحيح. يقدم العرض التقديمي كتابة تطبيقات الويب الآمنة وإضافات Chrome من مؤتمر Google I/O لعام 2012 بعض الأمثلة الجيدة عن هذه الأساليب عمليًا، ويستغرق قضاء 56 دقيقة من وقتك.