Website-Pflichtencheckby Jurono
SecurityWebsiteCodeTechnicalMaintenance

postMessage Is an API Boundary: Audit Iframe and Popup Messaging

Websites use window.postMessage to exchange data with iframes, login popups, payment windows, and widgets. Audit targetOrigin, event.origin, event.source, message schemas, and privileged actions.

By Jurono
Updated: August 26, 2026

An embedded booking widget reports “appointment complete.” A payment window sends a status back to the parent page. A login popup announces that authentication finished. An editor runs inside an iframe and sends content to the surrounding dashboard.

These are exactly the kinds of workflows window.postMessage() was designed for. The browser API enables controlled communication between Window contexts, including across origin boundaries. That is also why it should not be treated as a harmless internal event bus.

As soon as a message can trigger an action, change state, or transport sensitive data, it becomes a browser API boundary.

The useful audit question is therefore not only: “Does the widget work?” It is: “Who may send which message to whom, and what is that message allowed to do?”

The security model has two directions

With postMessage, there are two separate trust decisions.

The sending page decides which origin may receive the message. That is the role of targetOrigin. The WHATWG HTML Standard specifies that the message is discarded if the target window's current origin does not match the supplied target origin. Using * removes that restriction.

The receiving page decides which sender it trusts. A MessageEvent exposes event.origin and, where useful, event.source. MDN explicitly recommends verifying the sender's identity and then validating the syntax of the received message.

Both directions matter. A careful receiver does not help if the sender transmits sensitive data with targetOrigin: "*" to a window that has since navigated to an unrelated site. And an exact target origin does not help if the receiving listener accepts messages from every sender.

Red flag 1: every targetOrigin is *

* is convenient because delivery does not depend on the destination window's current origin. That convenience is also the risk.

MDN recommends specifying the exact target origin when the expected destination is known. The match is exact and includes scheme, hostname, and port where applicable.

Why does this matter? A Window object can navigate to a different document during its lifetime. A popup might begin on your domain and later redirect elsewhere. If your page keeps sending confidential messages with *, it is no longer constraining the identity of the receiver.

Review every postMessage call:

  • Is the destination truly variable, or do we know its origin?
  • Do we use an explicit HTTPS origin?
  • Does that value come from controlled configuration rather than an untrusted query parameter?
  • Can the destination navigate before the message is sent?
  • Does the payload contain anything another document should not receive?

A wildcard is not automatically a vulnerability. It is, however, a deliberate permission for whatever origin currently owns that target browsing context, and it should be explainable as such.

Red flag 2: the receiver never checks event.origin

A common listener conceptually does this: receive a message, inspect event.data.type, execute an action.

That skips the primary sender-identity check. OWASP recommends checking the sender origin on every received web message, and MDN notes that event.source can be useful as an additional identity signal.

This matters because your intended widget is not the only context that may be able to send a message. Windows inside a known iframe or popup hierarchy can obtain references to one another. The receiver therefore has to decide which sender is acceptable.

Use exact origins such as https://payments.example.com. Fuzzy string checks such as origin.includes("example.com") or careless suffix matching are unsafe because attacker-controlled hostnames can be constructed to match the substring. OWASP explicitly recommends exact matching against the fully qualified origins you expect.

Red flag 3: a matching origin makes the payload “trusted”

event.origin answers an origin question. It does not validate message contents.

If an allowed origin has an XSS issue, loads a compromised third-party script, or hosts several applications with different trust levels, code running there may also be able to send messages. OWASP therefore recommends treating event.data as untrusted input and validating it.

A resilient message protocol defines things such as:

  • allowed message types;
  • required fields per type;
  • data types and maximum lengths;
  • allowed enum values;
  • protocol versions;
  • request or correlation IDs;
  • which state a message may change;
  • which responses are valid for which requests.

The secure question is not “Did this object come from our partner domain?” It is: “Did it come from the expected context, does it conform to our protocol, and is this sender allowed to request this operation?”

Red flag 4: message data becomes HTML or code

Cross-window messaging transfers data. It should not become a shortcut to code execution.

OWASP specifically warns against evaluating message data with eval() or placing it directly into the DOM with innerHTML. Either pattern can turn a message channel into a DOM XSS path.

If you need to display text, textContent is often the correct primitive. If structured data needs rendering, use the normal template or component model. If the protocol genuinely carries HTML, that path needs the same sanitisation and trusted-content controls as every other HTML input.

Data does not become safe merely because it arrived from an iframe instead of a form field.

Red flag 5: a message can trigger privileged actions

The audit becomes especially important when a message does more than synchronise UI state. Examples include:

  • “complete order”;
  • “delete document”;
  • “connect account”;
  • “accept address”;
  • “store token”;
  • “open admin view”;
  • “mark payment as successful.”

At that point, the message handler is functionally an API endpoint.

A browser message should never be the only authorization for a server-relevant action. If the handler then calls your API, the server must still verify the session, role, permission, and business preconditions. A message such as { type: "payment-success" } is not proof of payment. A message such as { type: "delete-user" } is not authorization.

This is the difference between a signal and evidence. The browser can signal that something may have happened. Critical state should be confirmed from the authoritative source responsible for it.

event.source is the often-forgotten second identity

Imagine a page with several iframes from the same allowed origin. An origin check alone cannot distinguish which specific window sent a message.

event.source is a reference to the sending Window. In a controlled integration, the receiver can compare it with a known reference such as paymentFrame.contentWindow.

Not every integration needs this extra check. But when several windows share the same allowed origin while having different capabilities, it creates a useful second boundary.

Build a message protocol instead of an event fog

PostMessage integrations often grow organically. First there is ready, then resize, later success, close, setToken, and refresh, until nobody can describe the allowed state machine.

Treat the interface as a small API instead.

1. Define origins centrally

Expected origins should come from controlled configuration. Development, staging, and production can have different values, but they should not be copied from arbitrary request or query parameters.

2. Version the protocol

A field such as version: 1 makes it easier to run old and new widget versions in parallel without silently changing the meaning of existing fields.

3. Use a discriminated message schema

Give every message a clear type such as widget.ready, widget.resize, or checkout.completed, and validate a dedicated payload for that type. Unknown message types should be ignored or safely logged.

4. Keep capabilities narrow

An iframe that only needs to report its height does not need a universal execute message. Small, specific operations are easier to review and harder to misuse.

5. Correlate responses

For request/response patterns, random request IDs help bind a response to the currently outstanding request. This does not replace origin validation, but it prevents arbitrary stale messages from being mistaken for a response to a new operation.

6. Keep listeners alive only as long as necessary

A global message listener that remains active for the rest of the session after a popup closes creates needless attack surface. Register and remove listeners according to the real lifecycle of the feature.

Iframes also need an embedding strategy

postMessage and iframe security are closely related, but they are not the same control.

The iframe sandbox attribute can remove capabilities from an embedded document. Content Security Policy can constrain which frames your page loads and which sites may embed your own pages. These controls do not replace message validation, but they reduce the number and power of contexts that can participate in the integration.

Sandboxed or opaque origins create edge cases where a precise target origin may not be available. MDN notes that data: URLs have opaque origins and may require * when sending to them. Exceptions like this should not weaken the whole protocol. If targetOrigin cannot provide the restriction, minimise the data, validate event.source where possible, enforce strict message schemas, and keep capabilities narrow.

A practical 30-minute audit

For a first inventory, search the frontend for:

  • postMessage(;
  • addEventListener("message";
  • onmessage;
  • contentWindow;
  • window.open;
  • embedded third-party widgets and iframes.

Then build a small table: sender, receiver, allowed origin, message types, sensitive data, actions triggered, origin check, source check, schema validation.

Test the most important channels in a browser:

  1. What happens when a message arrives from the wrong origin?
  2. What happens when the origin is allowed but the payload is malformed?
  3. Are unknown message types rejected?
  4. Can another same-origin frame trigger the same operation?
  5. Is sensitive data ever sent with *?
  6. Can the target navigate before the message is sent?
  7. Can a message cause a server-side change without server-side authorization being checked again?
  8. Is message data interpreted as HTML or code?
  9. Do listeners stay registered longer than necessary?
  10. Are production and preview origins clearly separated?

This short exercise often finds more meaningful issues than a generic security-header score because it inspects real application logic.

What Website-Pflichtencheck would inspect

A technical review of embedded services and cross-window communication can inspect:

  • every postMessage sender and message listener;
  • explicit target origins and wildcards;
  • exact event.origin checks;
  • appropriate use of event.source;
  • message and schema validation;
  • DOM XSS paths created by message payloads;
  • privileged actions and server-side authorization;
  • iframe sandboxing, CSP, and allowed embedding sources;
  • popup and redirect lifecycles;
  • differences between production, staging, and preview;
  • third-party widgets whose origin or integration model changed;
  • listener lifecycle and cleanup;
  • tests for wrong origins, malformed payloads, and unexpected windows.

The objective is not to avoid postMessage. The API exists precisely because modern sites need controlled communication across origin boundaries.

But a message listener is not an internal function merely because it runs in frontend code. If other windows can reach it, it deserves the same care as every other API input.

Jurono logo

Jurono

Technical website audits, website fixes, and AI code rescue for small businesses, practices, law firms, and founders in Germany.

Get our free security checklist before you go.

Download free PDF

Want a first signal in 30 seconds? Run the free website quick test.

Get website notes by email

One short technical note every two weeks. No spam, no sales pitch.

Matching offers

Move forward directly

Based on the topics in this article — without a long search.

Manual Website Check

When nobody is sure which scripts, cookie signals, or technical risks are currently running on the site.

249

Manual technical first assessment and clear priorities within two business days.

  • Quickly see whether tracking, cookies, external services, or HTTPS look suspicious
  • Mobile, load time, and technical issues explained in plain language
  • The most important points in a short priority list
Get clarity with Manual Website Check

Technical Website Audit

When the website matters, but nobody knows which visible required areas, technical risks, and fixes actually have priority.

549

Audit, assessment, and concrete action plan within 3-5 business days.

  • Everything from the manual website check, assessed and documented in more depth
  • Concrete findings for cookie, tracking, and external service signals
  • Visible required areas checked technically, without legal advice
Start Technical Website Audit

Website Protection & Maintenance

For small businesses without an internal web team that need ongoing technical calm instead of occasional emergencies.

279/month

Monthly technical support after a short onboarding check.

  • Updates and backups supported in a controlled way depending on system access
  • Monthly short check for new technical findings
  • Up to 90 minutes of small changes or fixes per month
Secure Website Protection & Maintenance

Get clarity before you commit to fixes.

Start with a technical check. If the findings are minor, you can stop there, hand the report to your existing team, or book targeted fixes later.

Technical audit and implementation, not legal advice. I check visible signals, integrations, and delivery issues; legal texts and binding legal assessments remain the work of lawyers or privacy consultants.

postMessage Is an API Boundary: Audit Iframe and Popup Messaging