Description
The window.postMessage API enables cross-origin communication between browser windows, iframes, and tabs. It is the legitimate mechanism used by OAuth flows, payment widgets, chat embeds, and analytics integrations to pass data between frames from different origins. When message handlers receive and act on postMessage events without verifying the sender's origin, any page that can load the target in an iframe—or open it in a popup—can send arbitrary commands to it.
CWE-346 (Origin Validation Error) applies because the receiving handler fails to verify that the message originates from a trusted source before processing it. The vulnerability manifests in two forms: an insecure message receiver that processes events from any origin, and an insecure sender that posts messages to "*" (any destination) instead of a specific target origin, potentially leaking sensitive data to a malicious framing page.
These vulnerabilities frequently appear in single-page applications with embedded payment widgets, document viewers, map integrations, or any feature using third-party iframes. They also affect browser extensions that use postMessage to communicate between content scripts and background pages.
How It Works
A vulnerable message handler accepts and acts on messages from any origin:
// Vulnerable: no origin check
window.addEventListener('message', function(event) {
// Blindly trust the message data
if (event.data.action === 'navigate') {
window.location.href = event.data.url; // Open redirect
}
if (event.data.action === 'setToken') {
localStorage.setItem('authToken', event.data.token); // Token injection
}
});
An attacker creates a page that iframes the target and sends a malicious postMessage:
<!-- Attacker's page -->
<iframe id="target" src="https://app.example.com/dashboard"></iframe>
<script>
document.getElementById('target').onload = function() {
this.contentWindow.postMessage(
{ action: 'navigate', url: 'https://attacker.example.com/phish' },
'*' // Target origin wildcard — message reaches any origin
);
};
</script>
The reverse is also dangerous—an application that sends sensitive data to "*":
// Vulnerable sender: leaks token to any framing page
window.parent.postMessage({ token: userAccessToken }, '*');
// An attacker's framing page receives this:
window.addEventListener('message', function(e) {
console.log('Stolen token:', e.data.token);
});
Penetration testers use Burp Suite's DOM Invader feature to automatically instrument postMessage handlers and identify sink functions that process message data without origin validation.
// Manual origin check — correct implementation
window.addEventListener('message', function(event) {
if (event.origin !== 'https://trusted-partner.example.com') {
return; // Reject messages from untrusted origins
}
// Safe to process event.data
});
Impact
- Open redirect — Injected navigation commands redirect users to attacker-controlled phishing pages within the trusted application context.
- Cross-site scripting — Message data inserted into the DOM without sanitization enables script injection via the postMessage channel.
- Authentication bypass — Token injection via postMessage overwrites legitimate session tokens with attacker-controlled values.
- Sensitive data exfiltration — Applications that send sensitive data (tokens, PII) to the wildcard target
"*"leak that data to any framing page. - UI manipulation — Malicious messages alter displayed content, modify form values, or trigger privileged actions within the embedded application.
Detection
- Use Burp Suite's DOM Invader to automatically hook
window.postMessageandaddEventListener('message', ...)calls, then send test payloads to identify unvalidated handlers. - Review JavaScript source bundles for
addEventListener('message'and inspect the handler body for the presence of anevent.origincheck with strict equality. - Search for
postMessage(calls with the second argument set to'*'to identify senders that leak data to any origin. - Create an attacker-controlled page that iframes the target and sends
postMessagepayloads for various action types, observing whether the application processes them without authentication. - Test postMessage communication in OAuth flows, payment widget integrations, and any feature using cross-origin iframes, as these are the most common locations for insecure handlers.
Remediation
Always validate event.origin with strict equality. The receiver must verify the exact origin before processing any message:
window.addEventListener('message', function(event) {
if (event.origin !== 'https://expected-origin.example.com') {
return; // Silently ignore messages from unexpected origins
}
handleMessage(event.data);
});
Always specify the target origin in postMessage. Senders must specify the exact recipient origin, never the wildcard:
// Correct: specify exact target origin
iframe.contentWindow.postMessage(payload, 'https://embedded-app.example.com');
Validate and sanitize message data. Treat postMessage data as untrusted input regardless of origin. Validate message structure, sanitize string values before DOM insertion, and avoid passing message data to eval, innerHTML, or navigation sinks.
Implement a message schema. Define a strict schema for expected message formats and reject messages that do not conform, preventing injection of unexpected action types.
