Engineering Journal
Schema Editor
Schema Editor

postMessage With '*' Origin Sends Your Messages to Everyone. Use window.location.origin.

2026-06-04

TLDR

window.parent.postMessage(data, '') sends the message to any page that embeds your tool in an iframe, including malicious pages on different domains. The fix is one character: replace '' with window.location.origin. A typed bridge utility that routes all outbound messages through one function prevents the vulnerability from being re-introduced.


The Problem Class

Browser extensions, VS Code webviews, Electron apps, and iframe-embedded tools all use postMessage for cross-frame communication. Every developer building in these environments writes a postMessage call early in development. Many of them pass '*' as the target origin.

The '*' origin is convenient: it works immediately, requires no knowledge of the embedding page's origin, and is in most tutorial examples. It is also an OWASP-documented vulnerability: Cross-Document Messaging without Origin Validation (OWASP A07:2021 under Security Misconfiguration).

The Naive Approach

// signal that the tool is ready for the extension host
window.parent.postMessage({ type: 'tool:ready' }, '*');

This sends the message. The '*' origin means the browser delivers the message to the parent frame without checking what origin that frame is. Any page that embeds this tool in an <iframe> will receive the tool:ready message.

The tutorial justification for '*' is "use this when you don't know the parent's origin." This is the wrong conclusion. Not knowing the parent's origin means you should be more careful about what you send, not less.

Why It Breaks

Consider a VS Code webview that sends user data to the extension host:

// vulnerable: sends session token to any parent frame
window.parent.postMessage({
    type: 'tool:user-data',
    payload: { userId: user.id, token: user.sessionToken }
}, '*');

A malicious page that loads this webview in a hidden <iframe> will receive this message. The attacker gets the session token. The user never knows.

For read-only signals like tool:ready, the direct impact of '' may seem low. But '' on any message normalizes the pattern. The next developer adds a message with user data and copies the '*' from the existing example. The vulnerability escalates silently.

The broader issue: postMessage with '' bypasses the browser's same-origin policy. The same-origin policy prevents pages on different origins from reading each other's state. postMessage('') creates a voluntary exception to that policy with no access control.

The Better Model

Replace '*' with a specific origin for every postMessage call:

// fixed: only delivers to a parent on the same origin
window.parent.postMessage({ type: 'tool:ready' }, window.location.origin);

For VS Code webviews, the webview's origin is the VS Code extension origin. Use window.location.origin as the target when communicating with the extension host. The extension host's postMessage calls back to the webview use a similar restriction.

For cross-origin communication where you control both sides (a known external origin), specify the exact origin string:

window.parent.postMessage(data, 'https://known-parent-domain.com');

Validate incoming messages on the receiver side as well:

window.addEventListener('message', (e) => {
    // Reject messages from unexpected origins
    if (e.origin !== window.location.origin &&
        e.origin !== 'vscode-webview://') return;
    handleMessage(e.data);
});

A typed bridge utility routes all outbound messages through one function, making '*' a single auditable concern:

const Bridge = {
    _targetOrigin: window.location.origin,

send(type, payload) { try { window.parent.postMessage({ type, payload }, this._targetOrigin); } catch (_) {} },

onData(handler) { window.addEventListener('message', (e) => { if (e.origin !== this._targetOrigin) return; handler(e.data); }); } };

All postMessage calls in the codebase use Bridge.send(). The target origin is configured in one place. An audit of the entire postMessage surface is: check Bridge._targetOrigin.

Tradeoffs

Using window.location.origin breaks communication in development environments where the tool is loaded from file:// (origin is null) or from a different local port than the parent expects. The workaround is an explicit development origin in the bridge config, not falling back to '*'.

The One Thing to Watch For

VS Code webviews have a non-standard origin scheme (vscode-webview://extensionId/...). Messages from the extension host to the webview arrive with this origin. The webview's message event listener must accept this origin explicitly in addition to window.location.origin.

Read this post in the full Engineering Journal →