Insecure Cross-document Messaging (Wildcard Target Origin)

  • CWE 942
  • OWASP 2025-A02
  • WASC 15

The application uses the HTML5 cross-document messaging API (window.postMessage) in a way that does not constrain who can send or receive the message.

Two distinct mistakes are reported here:

1. Sending to any origin. A call such as target.postMessage(data, "*") tells the browser to deliver the message to whatever document currently occupies the target window. If that window has navigated, or was opened by an attacker, the payload - which frequently carries session identifiers, one-time tokens, profile data or internal state - is handed straight to the attacker's document.

2. Sending without a target origin. Calling postMessage(data) on a window reference without the second argument leaves the receiving document unrestricted in the same way.

3. Accepting from any origin. A message event handler that never inspects event.origin (or event.source) will act on messages sent by any page that can obtain a reference to the window - through an iframe, a popup, or window.opener. When the handler routes the message into eval, innerHTML, a router, a token store or an authenticated request, this becomes a fully controllable client-side entry point.

Impacts:

  • Disclosure of session tokens, CSRF tokens and personal data to an attacker-controlled document
  • Client-side code or HTML injection through an unvalidated message payload
  • Unauthorized state changes performed on behalf of the authenticated user
  • Bypass of the Same-Origin Policy that the rest of the application relies on
Remediation
  1. Always name the receiver. Replace "*" with the exact origin the message is meant for, for example frame.contentWindow.postMessage(payload, "https://app.example.com"). Build the value from a constant, never from document.referrer or from the message itself.

  2. Always validate the sender. Begin every message handler with an allow-list check:

window.addEventListener("message", function (event) {
    if (event.origin !== "https://app.example.com") {
        return;
    }
    // ...
});

Compare the full origin with a strict equality check. Do not use indexOf, startsWith or a regular expression, as https://app.example.com.evil.tld would pass those.

  1. Validate the payload. Treat the message data as untrusted input: check its shape and type, and never pass it to eval, Function, innerHTML, document.write or a dynamic import.

  2. Keep the channel narrow. Prefer MessageChannel / MessagePort when only two known parties need to talk, so no third document can join the conversation.

  3. Defense in depth. Restrict who may frame the application with Content-Security-Policy: frame-ancestors, and add X-Frame-Options for older browsers.

References