postMessage - легальний спосіб обміну даними між вікнами різних походжень: сторінка й вбудований <iframe> платіжної форми, вікно OAuth-входу й основний застосунок, віджет на чужому сайті й ваш сервер.
// відправник
iframe.contentWindow.postMessage({ type: 'resize', height: 640 }, 'https://widget.example');
// отримувач
window.addEventListener('message', (event) => {
if (event.origin !== 'https://app.example') return; // обов'язкова перевірка
if (event.data?.type === 'resize') {
iframe.style.height = `${Number(event.data.height)}px`;
}
});
Дві обов'язкові перевірки:
1. При відправленні - конкретне targetOrigin, а не '*'.
popup.postMessage({ token }, '*'); // небезпечно
Якщо вікно встигли перенаправити на інший сайт (наприклад, фрейм завантажив сторінку нападника), повідомлення з токеном отримає чужий код. З конкретним targetOrigin браузер просто не доставить повідомлення не тому адресату.
2. При отриманні - перевірка event.origin. Будь-яка сторінка, що має посилання на ваше вікно (відкрила його через window.open чи вбудувала у фрейм), може надіслати йому повідомлення. Без перевірки обробник виконає команди нападника.
Типові помилки:
- перевірка походження за підрядком:
event.origin.includes('example.com')пропуститьhttps://example.com.attacker.io. Лише точне порівняння з переліком; - довіра вмісту від довіреного походження: навіть повідомлення від свого фрейму - це дані, які могли прийти від користувача. Небезпечно передавати їх у
innerHTML,eval,location.href(схемаjavascript:); event.sourceбез перевірки - відповідати варто лише наevent.source, а не на «будь-яке вікно»;- чутливі дані в повідомленнях без потреби - кожне повідомлення може побачити розширення браузера чи інший слухач на тій самій сторінці;
- слухачі, що лишилися після закриття фрейму, - накопичуються в SPA.
Структура повідомлень: тип і версія в кожному повідомленні, валідація схеми (як для API). Повідомлення - це міжсайтовий API, і до нього ті самі вимоги.
Альтернатива для однієї вкладки свого ж походження - BroadcastChannel, а для запитів між вікнами з відповіддю - MessageChannel з окремими портами: менше слухачів на глобальному window.