Signatur
Beschreibung
postMessage() ist der offizielle Weg, um zwischen Fenstern mit unterschiedlichem Ursprung (Protokoll, Host, Port) Daten auszutauschen. Normalerweise verhindert die Same-Origin-Policy, dass ein Skript auf das DOM oder die Variablen eines fremden Fensters zugreift. Über postMessage() kann ein Fenster dem anderen trotzdem gezielt eine Nachricht schicken, die dort als message-Event ankommt.
Typische Einsatzfälle sind eingebettete Widgets (Zahlungsformulare, Karten, Chat-Widgets) in einem <iframe>, OAuth-Anmeldungen per Pop-up, die das Ergebnis an window.opener zurückmelden, oder die Kommunikation zwischen einer App und einer Sandbox-Vorschau. Aufgerufen wird die Methode immer auf dem Zielfenster – etwa iframe.contentWindow, window.parent, window.opener oder dem Rückgabewert von window.open().
Die Nachricht wird mit dem Structured-Clone-Algorithmus kopiert. Du kannst also Objekte, Arrays, Map, Set, Date, Blob, ArrayBuffer usw. senden, aber keine Funktionen, DOM-Knoten oder Klasseninstanzen mit Prototyp. Übertragbare Objekte (ArrayBuffer, MessagePort, ImageBitmap …) lassen sich über transfer ohne Kopie weitergeben. Danach sind sie im Absenderkontext nicht mehr nutzbar.
Der Versand ist asynchron: Das Event wird erst ausgelöst, wenn alle ausstehenden Skripte im Zielfenster abgearbeitet sind. Beim Empfänger musst du event.origin und am besten auch event.source prüfen, bevor du den Daten vertraust.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $message Pflicht | any (structured-cloneable) | Die zu sendenden Daten. Sie werden per Structured Clone kopiert. Funktionen und DOM-Knoten sind nicht erlaubt und lösen einen DataCloneError aus. |
|
| $targetOrigin | string | "/" (bei Options-Variante) | Ursprung, den das Zielfenster haben muss, z. B. "https://example.com". Stimmt er nicht überein, wird die Nachricht still verworfen. "*" erlaubt jeden Ursprung (unsicher), "/" steht für den eigenen Ursprung. |
| $transfer | Transferable[] | [] | Liste übertragbarer Objekte (z. B. ArrayBuffer, MessagePort), deren Besitz an den Empfänger übergeht. |
| $options | { targetOrigin?: string, transfer?: Transferable[] } | { targetOrigin: "/" } | Alternative Schreibweise als Objekt mit targetOrigin und transfer. In neueren Browsern verfügbar. |
Beispiele
Eltern-Seite sendet Daten an ein eingebettetes iframe
// Auf https://shop.example
const frame = document.querySelector('#payment-frame');
frame.addEventListener('load', () => {
frame.contentWindow.postMessage(
{ type: 'INIT', amount: 49.9, currency: 'EUR' },
'https://pay.example' // nur an diesen Ursprung zustellen
);
});
iframe empfängt, prüft den Ursprung und antwortet
// Im iframe auf https://pay.example
const ALLOWED_ORIGIN = 'https://shop.example';
window.addEventListener('message', (event) => {
if (event.origin !== ALLOWED_ORIGIN) return; // fremde Absender ignorieren
const { type, amount, currency } = event.data ?? {};
if (type !== 'INIT') return;
console.log(`Zahlung vorbereitet: ${amount} ${currency}`);
// Antwort an genau das Fenster, das gesendet hat
event.source.postMessage({ type: 'READY' }, event.origin);
});
OAuth-Pop-up meldet Ergebnis an den Opener
// Hauptseite
const popup = window.open('https://auth.example/login', 'login', 'width=500,height=600');
const waitForToken = () => new Promise((resolve, reject) => {
const onMessage = (event) => {
if (event.origin !== 'https://auth.example' || event.source !== popup) return;
window.removeEventListener('message', onMessage);
event.data.error ? reject(new Error(event.data.error)) : resolve(event.data.token);
};
window.addEventListener('message', onMessage);
});
const token = await waitForToken();
console.log('Token erhalten:', token.slice(0, 8) + '…');
// Im Pop-up (auf https://auth.example):
// window.opener.postMessage({ token: 'abc123xyz789' }, 'https://app.example');
// window.close();
Options-Objekt und Transfer eines ArrayBuffer
const buffer = new ArrayBuffer(1024 * 1024); // 1 MB
const frame = document.querySelector('iframe');
frame.contentWindow.postMessage(
{ kind: 'image-data', buffer },
{ targetOrigin: 'https://viewer.example', transfer: [buffer] }
);
// Der Buffer gehört jetzt dem Empfänger
console.log(buffer.byteLength);
// Wichtig · Fallstricke
Sicherheit – Absender: Gib als targetOrigin immer einen konkreten Ursprung an, wenn du vertrauliche Daten sendest. Mit \"*\" kann die Nachricht bei einem Angreifer landen, falls das Zielfenster inzwischen auf eine andere Seite navigiert wurde, etwa ein umgeleitetes Pop-up.
Sicherheit – Empfänger:
- Prüfe in jedem
message-Handlerevent.origingegen eine Whitelist. Jedes Fenster kann dir Nachrichten schicken. - Prüfe zusätzlich
event.source, wenn du nur ein bestimmtes Fenster erwartest. - Behandle
event.dataals nicht vertrauenswürdige Eingabe: Validiere Typ und Struktur und gib die Daten niemals ungeprüft aninnerHTML,eval(),new Function()oderlocation.hrefweiter. Sonst drohen DOM-basiertes XSS und Open Redirects. - Vergleiche Ursprünge exakt mit
===. Prüfungen wieorigin.includes('example.com')oderendsWith()lassen sich mit Domains wieexample.com.evil.netumgehen.
Weitere Fallstricke:
- Ursprünge vergleichen Schema, Host und Port.
http://undhttps://sowiewww.und die Domain ohnewww.gelten als verschieden. Bei einer Abweichung wird die Nachricht still verworfen. - Sendest du vor dem
load-Event eines iframes, erhält eventuell noch das alte Dokument (about:blank) die Nachricht, und sie geht verloren. Zuverlässiger ist ein Handshake, bei dem das iframe ein „READY“ meldet. - Nicht klonbare Werte lösen einen
DataCloneErroraus. Klasseninstanzen verlieren ihren Prototyp. - Header wie
Cross-Origin-Opener-Policy: same-originkönnenwindow.openeraufnullsetzen. Dann funktioniert die Pop-up-Kommunikation nicht mehr. - Ein iframe mit
sandboxohneallow-same-originhat den Ursprung\"null\".
Kompatibilität: Die Variante mit targetOrigin funktioniert in allen Browsern. Die Options-Variante ({ targetOrigin, transfer }) gibt es erst in neueren Versionen von Chrome, Firefox und Safari. Für ältere Browser nimm die Variante mit Positionsparametern. Für Worker und MessageChannel gibt es eigene postMessage()-Methoden mit leicht abweichender Signatur (ohne targetOrigin).