Engineering Journal
Schema Editor
Schema Editor

postMessage Sends Messages to Any Embedder When the Origin Is '*'

2026-06-04

TLDR

window.parent.postMessage(data, '') delivers messages to any parent frame regardless of origin. An attacker who loads your tool in an <iframe> receives all your messages. Replace '' with window.location.origin for same-origin parent communication. One character change, all messages now have a defined audience.


Symptom

A security audit or review identifies postMessage calls with '*' as the target origin. The tool works correctly and no attack has been observed. The vulnerability is structural: the channel sends messages to any receiver, not just the intended one.

Why It Happens

The postMessage API requires a target origin as the second argument. The '' value is valid and means "deliver to any origin." Tutorial examples use '' because it always works, regardless of the parent page's origin. Developers copy the pattern because it is in the example and the call works.

The security implication: '' does not mean "any same-origin frame." It means any frame, on any domain, with any security context. The browser's same-origin policy is bypassed for messages sent with ''.

// vulnerable: any page embedding this in an iframe receives the message
window.parent.postMessage({ type: 'tool:ready', data: payload }, '*');

// safe: only the same-origin parent receives it window.parent.postMessage({ type: 'tool:ready', data: payload }, window.location.origin);

The Fix

// Replace every postMessage with '*' origin:
// BEFORE
window.parent.postMessage(data, '*');

// AFTER window.parent.postMessage(data, window.location.origin);

Also validate incoming messages on the receiver side:

// BEFORE: no origin check
window.addEventListener('message', (e) => {
    handleMessage(e.data);
});

// AFTER: reject messages from unexpected origins window.addEventListener('message', (e) => { if (e.origin !== window.location.origin) return; handleMessage(e.data); });

For VS Code webviews, the extension host's origin is a vscode-webview:// scheme. Accept it explicitly:

const ALLOWED_ORIGINS = new Set([
    window.location.origin,
    'vscode-webview://',
]);

window.addEventListener('message', (e) => { const allowed = [...ALLOWED_ORIGINS].some(o => e.origin.startsWith(o)); if (!allowed) return; handleMessage(e.data); });

How to Prevent It

Add a grep rule to your CI or pre-commit hooks:

# fail if any postMessage call uses '*' origin
grep -r "postMessage(" src/ | grep "'\*'" && exit 1

Or extract all postMessage calls to a single bridge module and make direct window.parent.postMessage calls a lint error.

The Generalizable Lesson

postMessage with '*' is not a convenience shortcut. It is a deliberate bypass of the same-origin policy. Every postMessage call is a security decision: who is the intended receiver? Use the most specific origin string that is correct for your communication pattern.

Read this post in the full Engineering Journal →