Wildcard target origin in postMessage

Data exposure through a wildcard postMessage target origin

Description

Setting targetOrigin to '*' in window.postMessage sends the message to the target window or iframe regardless of its origin. It does not broadcast to every window, but tokens or personal data may be exposed if the target can navigate to an attacker-controlled origin. Exploitability depends on the window reference, whether navigation is possible and the data being sent.

Potential impact

  • A malicious origin may receive tokens, session information or personal data.
  • Leaked authentication tokens or OAuth codes may allow impersonation or misuse of API permissions.
  • Commands or data intended for a trusted window may reach another party and disrupt payment or configuration workflows.

Remediation

  • Send confidential messages only to an exact origin consisting of the scheme, host and port. Use trusted configuration, such as https://example.com.
  • If several destinations are needed, select only an allowed origin and do not send when none matches. Reading the origin from an attacker-controlled iframe.src is insufficient.
  • Validate event.origin, event.source identifying the trusted window, and the message's type and fields at the receiver.
  • Minimize confidential data and restrict the purpose, recipient and lifetime of any necessary one-time token.
  • Fix the iframe destination to a trusted value and apply appropriate sandbox restrictions. Use noopener for new windows that do not need opener communication.

Examples

Before

javascript
// sender (parent page)
const iframe = document.getElementById("payFrame");
// Send the locally stored authentication token (confidential data)
const token = localStorage.getItem("auth_jwt");

// Using '*' lets the target receive the message at any origin
iframe.contentWindow.postMessage({ type: "AUTH", token }, "*");

After

javascript
// sender (parent page)
const iframe = document.getElementById("payFrame");
// Fix the target origin in trusted configuration
const TARGET_ORIGIN = "https://pay.example.com";

// Avoid confidential data or minimize it, for example with a one-time code
const message = { type: "REQUEST_PAYMENT", orderId: "ORD-2025-0912-001" };
iframe.contentWindow.postMessage(message, TARGET_ORIGIN);

// receiver (child iframe page)
window.addEventListener("message", (event) => {
  // Accept only the expected parent origin
  if (event.origin !== "https://app.example.com") return;
  if (event.source !== window.parent) return;
  // Validate the message shape and type
  if (!event.data || event.data.type !== "REQUEST_PAYMENT") return;
  if (typeof event.data.orderId !== "string") return;

  // Separately verify order access and payment conditions on the server
  processPayment(event.data.orderId);
});

In the first example, the target iframe can receive the authentication token after navigating to an attacker's origin. The second fixes the target origin and checks the origin, parent window and message fields at the receiver. The sender and receiver code run on separate pages. The omitted processPayment implementation must verify access to the order and payment conditions on the server.

References