Insecure Cross-document Messaging (Missing 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
Always name the receiver. Replace
"*"with the exact origin the message is meant for, for exampleframe.contentWindow.postMessage(payload, "https://app.example.com"). Build the value from a constant, never fromdocument.referreror from the message itself.Always validate the sender. Begin every
messagehandler 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.
Validate the payload. Treat the message data as untrusted input: check its shape and type, and never pass it to
eval,Function,innerHTML,document.writeor a dynamicimport.Keep the channel narrow. Prefer
MessageChannel/MessagePortwhen only two known parties need to talk, so no third document can join the conversation.Defense in depth. Restrict who may frame the application with
Content-Security-Policy: frame-ancestors, and addX-Frame-Optionsfor older browsers.