Коротка відповідь
Повідомлення — недовірений запит до межі можливостей. Перевіряйте sender, тип, поля, розміри й операцію до привілейованих API. Обирайте getFeatureStatus чи lookupItem(id), не fetchURL, executeCode або callChromeAPI. Runtime-канал не робить дані сторінки довіреними.
Worker володіє привілейованими рішеннями й повертає мінімум. Інспектор читає DOM і надсилає обмежений ID, а worker будує дозволений URL. Посібник ізоляції пояснює DOM; тут — авторизація й контракт receiver.
Практичні кроки реалізації
Назвіть операції контракту
Використовуйте дискримінований тип і відхиляйте невідоме. Визначте успіх і помилку без сирих exceptions. Версіонуйте, якщо контексти випусків співіснують. Не дозволяйте caller вибирати методи чи nested-шляхи привілейованого простору.
Перевірте потрібного caller
Для content-script перевірте extension ID, tab і URL чи origin. Додайте frame-обмеження для головного фрейму. Для extension-page-only вимагайте відповідну поверхню. Externally connectable потребує окремих правил і listener, не випадкового повтору внутрішнього.
Перевірте ввід і можливість
Перевіряйте unknown, масиви, довжину, integers та ID. Схема не авторизує запис; перевірте користувача чи tab. Повторно звірте permissions і обробіть races. Не повертайте токени, серверні помилки чи сторонні записи сторінковому caller.
Свідомо обробіть асинхронну відповідь
Поведінка залежить від версії Chrome. Callback із literal true тримає канал для асинхронного sendResponse. Один responder, обробка rejection і timeout обов’язкові. Chrome серіалізує JSON, тому функції, DOM і особливі типи не передаються незмінними.
Приклад реалізації
Worker приймає вузький status-запит від top-frame скрипта умовного origin. Він читає preference без всього storage. Маніфесту потрібен storage, а скрипту — відповідний доступ.
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
let allowed = false;
try {
allowed = sender.id === chrome.runtime.id &&
typeof sender.tab?.id === "number" && sender.frameId === 0 &&
new URL(sender.url ?? "").origin === "https://app.example.com";
} catch { /* Invalid or absent sender URL remains disallowed. */ }
if (!allowed || !message || typeof message !== "object" ||
message.type !== "getFeatureStatus" || Object.keys(message).length !== 1) {
return false;
}
chrome.storage.local.get("featureEnabled").then(
result => sendResponse({ ok: true, enabled: result.featureEnabled === true }),
() => sendResponse({ ok: false, code: "UNAVAILABLE" }),
);
return true;
});
Валідатор і sender-список — окремі перевірки. Для даних додайте ownership і permissions в async-шлях. Origin умовний. False для чужих повідомлень дозволяє іншим listener власні контракти.
Компроміси та альтернативи
Разові повідомлення підходять request-response. Ports — тривалим розмовам із disconnect, межами й очищенням. Port не робить payload довіреним. Зберігайте однакову політику та не тримайте port лише для життя worker.
Поширені помилки
- Довільний fetch URL.
- TypeScript як runtime-перевірка.
- Ігнорування frame чи URL.
- Усе storage чи сирі помилки скрипту.
- Забутий true для async-callback.
Контрольний список перед випуском
- Команди й відповіді явні.
- Sender відповідає поверхні.
- Ввід обмежений.
- Доступ перевірено.
- Async і закриття оброблено.
- Відповідь мінімальна.
Поширені запитання
Чи вистачає extension ID?
Він визначає контекст, не потрібний tab, frame чи ввід. Додайте вужчі перевірки функції.
Чи worker має повернути весь fetch?
Зазвичай повертайте parsed-валідну проєкцію. Це зменшує розкриття й незалежність від сервісних деталей.
Висновок
Зробіть receiver вузьким API. Перевірте хто, що, право та мінімальний результат. Послідовність потрібна навіть sender одного розширення.
Первинні джерела
Пов’язані посібники та приклади
Потрібно реалізувати це у вашому продукті?
Перегляньте відповідні приклади продуктів або поділіться контекстом розробки вебзастосунку, розширення Chrome, API чи SaaS.