XMLHttpRequest متعدد المصادر

يمكن لصفحات الويب العادية استخدام كائن XMLHttpRequest لإرسال البيانات واستلامها من جهاز التحكّم عن بُعد. على الرغم من أنّها مقيَّدة بسياسة المصدر نفسها. النصوص البرمجية للمحتوى تبدأ الطلبات نيابةً عن مصدر الويب الذي تم إدخال النص البرمجي للمحتوى فيه، وبالتالي المحتوى تخضع النصوص البرمجية أيضًا لسياسة المصدر نفسها. (خضعت النصوص البرمجية للمحتوى لـ CORB) بدءًا من Chrome 73 وCORS منذ الإصدار Chrome 83). أصول الإضافات ليست محدودة للغاية - نص برمجي التي يتم تنفيذها في صفحة الخلفية أو علامة التبويب التي تعمل في المقدّمة التحدّث إلى الخوادم البعيدة خارج مصدرها، طالما أن الإضافة تطلب أذونات من مصادر متعددة.

مصدر الإضافة

توجد كل إضافة قيد التشغيل ضمن مصدر الأمان المنفصل الخاص بها. بدون طلب المزيد الإضافية، يمكن للإضافة استخدام XMLHttpRequest للحصول على موارد ضمن تثبيتها. بالنسبة على سبيل المثال، إذا كانت إحدى الإضافات تحتوي على ملف إعداد JSON يُسمى config.json، في config_resources، يمكن للإضافة استرداد محتوى الملف على النحو التالي:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.
xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
xhr.send();

إذا كانت الإضافة تحاول استخدام مصدر أمان غير نفسه، يُرجى قول https://www.google.com، لا يسمح المتصفّح بذلك ما لم تطلب الإضافة المصدر المشترك المناسب الأذونات.

طلب أذونات من مصادر متعددة

من خلال إضافة مضيفين أو أنماط مطابقة المضيف (أو كليهما) إلى قسم الأذونات في manifest، يمكن للإضافة طلب الوصول إلى الخوادم البعيدة خارج مصدرها.

{
  "name": "My extension",
  ...
  "permissions": [
    "https://www.google.com/"
  ],
  ...
}

يمكن أن تكون قيم الأذونات من مصادر متعددة عبارة عن أسماء مضيفين مؤهَّلة بالكامل، مثل ما يلي:

  • "https://www.google.com/"
  • "https://www.gmail.com/"

أو يمكن أن تكون أنماط مطابقة، مثل ما يلي:

  • "https://*.google.com/"
  • "https://*/"

نمط مطابقة "https://*/" يتيح وصول HTTPS إلى كل النطاقات التي يمكن الوصول إليها. لاحظ أنه هنا، قم بمطابقة تشبه أنماط تطابق النص البرمجي للمحتوى، ولكن أي معلومات مسار تتبع المضيف الذي يتم تجاهله.

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

"permissions": [
  "http://www.google.com/",
  "https://www.google.com/"
]

الاعتبارات الأمنية

تجنب ثغرات البرمجة النصية على المواقع الإلكترونية

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

var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // WARNING! Might be evaluating an evil script!
    var resp = eval("(" + xhr.responseText + ")");
    ...
  }
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // WARNING! Might be injecting a malicious script!
    document.getElementById("resp").innerHTML = xhr.responseText;
    ...
  }
}
xhr.send();

بدلاً من ذلك، يمكنك تفضيل واجهات برمجة التطبيقات الأكثر أمانًا التي لا تشغّل النصوص البرمجية:

var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // JSON.parse does not evaluate the attacker's scripts.
    var resp = JSON.parse(xhr.responseText);
  }
}
xhr.send();
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // innerText does not let the attacker inject HTML elements.
    document.getElementById("resp").innerText = xhr.responseText;
  }
}
xhr.send();

حصر الوصول إلى النصوص البرمجية للمحتوى على الطلبات الواردة من عدّة مصادر

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

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

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
      if (request.contentScriptQuery == 'fetchUrl') {
        // WARNING: SECURITY PROBLEM - a malicious web page may abuse
        // the message handler to get access to arbitrary cross-origin
        // resources.
        fetch(request.url)
            .then(response => response.text())
            .then(text => sendResponse(text))
            .catch(error => ...)
        return true;  // Will respond asynchronously.
      }
    });
chrome.runtime.sendMessage(
    {contentScriptQuery: 'fetchUrl',
     url: 'https://another-site.com/price-query?itemId=' +
              encodeURIComponent(request.itemId)},
    response => parsePrice(response.text()));

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

بدلاً من ذلك، صمِّم معالِجات الرسائل التي تحدّ من الموارد التي يمكن جلبها. أدناه، لن يتم تضمين سوى يتم توفير itemId من خلال النص البرمجي للمحتوى، وليس من عنوان URL الكامل.

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
      if (request.contentScriptQuery == 'queryPrice') {
        var url = 'https://another-site.com/price-query?itemId=' +
            encodeURIComponent(request.itemId);
        fetch(url)
            .then(response => response.text())
            .then(text => parsePrice(text))
            .then(price => sendResponse(price))
            .catch(error => ...)
        return true;  // Will respond asynchronously.
      }
    });
chrome.runtime.sendMessage(
    {contentScriptQuery: 'queryPrice', itemId: 12345},
    price => ...);

تفضيل HTTPS على HTTP

بالإضافة إلى ذلك، يجب توخي الحذر بشكل خاص بشأن الموارد التي يتم استردادها عبر HTTP. إذا تم استخدام الإضافة على شبكة العدائية، فإن مهاجم الشبكة (المعروف أيضًا باسم "man-in-the-middle") يمكنه تعديل الرد وربما مهاجمة الإضافة. وبدلاً من ذلك، يُفضّل استخدام بروتوكول HTTPS كلما أمكن ذلك.

تعديل سياسة أمان المحتوى

إذا عدّلت سياسة أمان المحتوى التلقائية للتطبيقات أو الإضافات عن طريق إضافة content_security_policy إلى ملف البيان، فينبغي عليك التأكد من أن أي مضيفات يمكن الوصول إليها التي تريد الاتصال بها مسموح بها. على الرغم من أنّ السياسة التلقائية لا تحصر الاتصالات بالمضيفات، يُرجى توخي الحذر عند إضافة التوجيه connect-src أو default-src بشكل صريح.