صفحات وب معمولی می توانند از fetch()
یا API های XMLHttpRequest
برای ارسال و دریافت داده از سرورهای راه دور استفاده کنند، اما آنها توسط همان خط مشی مبدا محدود شده اند. اسکریپتهای محتوا درخواستهایی را از طرف منبع وب که اسکریپت محتوا به آن تزریق شده است، آغاز میکنند و بنابراین اسکریپتهای محتوا نیز مشمول همان خطمشی مبدا هستند. ریشه های پسوند چندان محدود نیستند. یک اسکریپت در حال اجرا در یک کارگر خدمات افزودنی یا برگه پیش زمینه می تواند با سرورهای راه دور خارج از مبدأ خود صحبت کند، تا زمانی که برنامه افزودنی مجوزهای مبدأ متقاطع را درخواست کند.
منشا پسوند
هر برنامه افزودنی در حال اجرا در منشا امنیتی جداگانه خود وجود دارد. بدون درخواست امتیازات اضافی، برنامه افزودنی می تواند fetch()
برای دریافت منابع در داخل نصب خود فراخوانی کند. به عنوان مثال، اگر یک برنامه افزودنی حاوی یک فایل پیکربندی JSON به نام config.json
در یک پوشه config_resources/
باشد، پسوند میتواند محتویات فایل را مانند این بازیابی کند:
const response = await fetch('/config_resources/config.json');
const jsonData = await response.json();
اگر برنامه افزودنی بخواهد از منبع امنیتی دیگری غیر از خودش استفاده کند، مثلاً https://www.google.com، مرورگر آن را مجاز نمیداند، مگر اینکه برنامه افزودنی مجوزهای متقاطع مناسب را درخواست کرده باشد.
درخواست مجوزهای متقاطع
برای درخواست دسترسی به سرورهای راه دور خارج از مبدا برنامه افزودنی، میزبان ها، الگوهای مطابقت یا هر دو را به بخش host_permissions فایل مانیفست اضافه کنید.
{
"name": "My extension",
...
"host_permissions": [
"https://www.google.com/"
],
...
}
مقادیر مجوز متقاطع می توانند نام میزبان کاملا واجد شرایط باشند، مانند موارد زیر:
- "https://www.google.com/"
- "https://www.gmail.com/"
یا می توانند الگوهای مطابقت باشند، مانند موارد زیر:
- "https://*.google.com/"
- "https://*/"
الگوی مطابقت "https://*/" به HTTPS اجازه دسترسی به همه دامنه های قابل دسترسی را می دهد. توجه داشته باشید که در اینجا، الگوهای تطبیق مشابه الگوهای تطبیق اسکریپت محتوا هستند، اما هر گونه اطلاعات مسیری که دنبال میزبان است نادیده گرفته میشود.
همچنین توجه داشته باشید که دسترسی هم توسط میزبان و هم توسط طرح اعطا می شود. اگر یک برنامه افزودنی میخواهد هم به HTTP ایمن و هم غیرایمن دسترسی داشته باشد به یک میزبان یا مجموعهای از میزبانها، باید مجوزها را جداگانه اعلام کند:
"host_permissions": [
"http://www.google.com/",
"https://www.google.com/"
]
Fetch() در مقابل XMLHttpRequest()
fetch()
به طور خاص برای کارگران خدمات ایجاد شده است و از یک روند وب گسترده تر به دور از عملیات همزمان پیروی می کند. API XMLHttpRequest()
در برنامههای افزودنی خارج از سرویسکار پشتیبانی میشود، و فراخوانی آن، کنترلکننده واکشی کارگر سرویس افزودنی را فعال میکند. کار جدید باید هر جا که امکان دارد fetch()
استفاده کند.
ملاحظات امنیتی
از آسیب پذیری های اسکریپت بین سایتی اجتناب کنید
هنگام استفاده از منابع بازیابی شده از طریق fetch()
، سند خارج از صفحه، پانل کناری یا پنجره بازشو باید مراقب باشند که قربانی اسکریپت های متقابل سایت نشوند. به طور خاص، از استفاده از APIهای خطرناک مانند innerHTML
اجتناب کنید. مثلا:
const response = await fetch("https://api.example.com/data.json");
const jsonData = await response.json();
// WARNING! Might be injecting a malicious script!
document.getElementById("resp").innerHTML = jsonData;
...
در عوض، API های امن تری را ترجیح دهید که اسکریپت ها را اجرا نمی کنند:
const response = await fetch("https://api.example.com/data.json");
const jsonData = await response.json();
// JSON.parse does not evaluate the attacker's scripts.
let resp = JSON.parse(jsonData);
const response = await fetch("https://api.example.com/data.json");
const jsonData = response.json();
// textContent does not let the attacker inject HTML elements.
document.getElementById("resp").textContent = jsonData;
محدود کردن دسترسی اسکریپت محتوا به درخواست های متقابل
هنگام انجام درخواستهای متقاطع از طرف یک اسکریپت محتوا، مراقب باشید در برابر صفحات وب مخربی که ممکن است سعی در جعل هویت یک اسکریپت محتوا داشته باشند، محافظت کنید . به ویژه، اجازه ندهید اسکریپت های محتوا درخواست 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') {
const 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
مراقب باشید.
صفحات وب معمولی می توانند از fetch()
یا API های XMLHttpRequest
برای ارسال و دریافت داده از سرورهای راه دور استفاده کنند، اما آنها توسط همان خط مشی مبدا محدود شده اند. اسکریپتهای محتوا درخواستهایی را از طرف منبع وب که اسکریپت محتوا به آن تزریق شده است، آغاز میکنند و بنابراین اسکریپتهای محتوا نیز مشمول همان خطمشی مبدا هستند. ریشه های پسوند چندان محدود نیستند. یک اسکریپت در حال اجرا در یک کارگر خدمات افزودنی یا برگه پیش زمینه می تواند با سرورهای راه دور خارج از مبدأ خود صحبت کند، تا زمانی که برنامه افزودنی مجوزهای مبدأ متقاطع را درخواست کند.
منشا پسوند
هر برنامه افزودنی در حال اجرا در منشا امنیتی جداگانه خود وجود دارد. بدون درخواست امتیازات اضافی، برنامه افزودنی می تواند برای دریافت منابع در داخل نصب خود fetch()
را فراخوانی کند. به عنوان مثال، اگر یک برنامه افزودنی حاوی یک فایل پیکربندی JSON به نام config.json
در یک پوشه config_resources/
باشد، پسوند میتواند محتویات فایل را مانند این بازیابی کند:
const response = await fetch('/config_resources/config.json');
const jsonData = await response.json();
اگر برنامه افزودنی بخواهد از منبع امنیتی دیگری غیر از خودش استفاده کند، مثلاً https://www.google.com، مرورگر آن را مجاز نمیداند، مگر اینکه برنامه افزودنی مجوزهای متقاطع مناسب را درخواست کرده باشد.
درخواست مجوزهای متقاطع
برای درخواست دسترسی به سرورهای راه دور خارج از مبدا برنامه افزودنی، میزبان ها، الگوهای مطابقت یا هر دو را به بخش host_permissions فایل مانیفست اضافه کنید.
{
"name": "My extension",
...
"host_permissions": [
"https://www.google.com/"
],
...
}
مقادیر مجوز متقاطع می توانند نام میزبان کاملا واجد شرایط باشند، مانند موارد زیر:
- "https://www.google.com/"
- "https://www.gmail.com/"
یا می توانند الگوهای مطابقت باشند، مانند موارد زیر:
- "https://*.google.com/"
- "https://*/"
الگوی مطابقت "https://*/" به HTTPS اجازه دسترسی به همه دامنه های قابل دسترسی را می دهد. توجه داشته باشید که در اینجا، الگوهای تطبیق مشابه الگوهای تطبیق اسکریپت محتوا هستند، اما هر گونه اطلاعات مسیری که دنبال میزبان است نادیده گرفته میشود.
همچنین توجه داشته باشید که دسترسی هم توسط میزبان و هم توسط طرح اعطا می شود. اگر یک برنامه افزودنی میخواهد هم به HTTP ایمن و هم غیرایمن دسترسی داشته باشد به یک میزبان یا مجموعهای از میزبانها، باید مجوزها را جداگانه اعلام کند:
"host_permissions": [
"http://www.google.com/",
"https://www.google.com/"
]
Fetch() در مقابل XMLHttpRequest()
fetch()
به طور خاص برای کارگران خدمات ایجاد شده است و از یک روند وب گسترده تر به دور از عملیات همزمان پیروی می کند. API XMLHttpRequest()
در برنامههای افزودنی خارج از سرویسکار پشتیبانی میشود، و فراخوانی آن، کنترلکننده واکشی کارگر سرویس افزودنی را فعال میکند. کار جدید باید هر جا که امکان دارد fetch()
استفاده کند.
ملاحظات امنیتی
از آسیب پذیری های اسکریپت بین سایتی اجتناب کنید
هنگام استفاده از منابع بازیابی شده از طریق fetch()
، سند خارج از صفحه، پانل کناری یا پنجره بازشو باید مراقب باشند که قربانی اسکریپت های متقابل سایت نشوند. به طور خاص، از استفاده از APIهای خطرناک مانند innerHTML
اجتناب کنید. مثلا:
const response = await fetch("https://api.example.com/data.json");
const jsonData = await response.json();
// WARNING! Might be injecting a malicious script!
document.getElementById("resp").innerHTML = jsonData;
...
در عوض، API های امن تری را ترجیح دهید که اسکریپت ها را اجرا نمی کنند:
const response = await fetch("https://api.example.com/data.json");
const jsonData = await response.json();
// JSON.parse does not evaluate the attacker's scripts.
let resp = JSON.parse(jsonData);
const response = await fetch("https://api.example.com/data.json");
const jsonData = response.json();
// textContent does not let the attacker inject HTML elements.
document.getElementById("resp").textContent = jsonData;
محدود کردن دسترسی اسکریپت محتوا به درخواست های متقابل
هنگام انجام درخواستهای متقاطع از طرف یک اسکریپت محتوا، مراقب باشید در برابر صفحات وب مخربی که ممکن است سعی در جعل هویت یک اسکریپت محتوا داشته باشند، محافظت کنید . به ویژه، اجازه ندهید اسکریپت های محتوا درخواست 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') {
const 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
مراقب باشید.