XMLHttpRequest متقاطع

صفحات وب معمولی می توانند از شی XMLHttpRequest برای ارسال و دریافت داده از سرورهای راه دور استفاده کنند، اما آنها توسط همان خط مشی مبدا محدود شده اند. اسکریپت‌های محتوا درخواست‌هایی را از طرف منبع وب که اسکریپت محتوا به آن تزریق شده است، آغاز می‌کنند و بنابراین اسکریپت‌های محتوا نیز مشمول همان خط‌مشی مبدا هستند. (اسکریپت‌های محتوا از Chrome 73 و CORS از Chrome 83 مشمول CORB بوده‌اند.) منشأ برنامه‌های افزودنی چندان محدود نیست - یک اسکریپت که در صفحه پس‌زمینه برنامه افزودنی یا برگه پیش‌زمینه اجرا می‌شود، می‌تواند با سرورهای راه دور خارج از مبدأ خود صحبت کند. برنامه افزودنی مجوزهای منبع متقابل را درخواست می کند.

منشا پسوند

هر برنامه افزودنی در حال اجرا در منشا امنیتی جداگانه خود وجود دارد. بدون درخواست امتیازات اضافی، برنامه افزودنی می تواند از 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، مرورگر آن را مجاز نمی‌داند، مگر اینکه برنامه افزودنی مجوزهای متقاطع مناسب را درخواست کرده باشد.

درخواست مجوزهای متقاطع

با افزودن میزبان ها یا الگوهای مطابقت میزبان (یا هر دو) به بخش مجوزهای فایل مانیفست ، برنامه افزودنی می تواند درخواست دسترسی به سرورهای راه دور خارج از مبدأ خود را داشته باشد.

{
  "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، صفحه پس زمینه شما باید مراقب باشد که قربانی اسکریپت نویسی بین سایتی نشود. به طور خاص، از استفاده از API های خطرناک مانند موارد زیر اجتناب کنید:

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();

در عوض، API های امن تری را ترجیح دهید که اسکریپت ها را اجرا نمی کنند:

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 باشید. اگر برنامه افزودنی شما در یک شبکه متخاصم استفاده می شود، یک مهاجم شبکه (معروف به "مرد در وسط" ) می تواند پاسخ را تغییر دهد و به طور بالقوه به برنامه افزودنی شما حمله کند. در عوض، در صورت امکان، HTTPS را ترجیح دهید.

تنظیم خط مشی امنیت محتوا

اگر با افزودن ویژگی content_security_policy به مانیفست خود ، خط‌مشی امنیت محتوای پیش‌فرض را برای برنامه‌ها یا برنامه‌های افزودنی تغییر دهید، باید مطمئن شوید که میزبان‌هایی که می‌خواهید به آن متصل شوید مجاز هستند. در حالی که خط‌مشی پیش‌فرض اتصالات را به میزبان‌ها محدود نمی‌کند، هنگام افزودن صریح دستورالعمل‌های connect-src یا default-src مراقب باشید.