# postMessage origin check bypass

Message listeners trusting any origin, referrer, or query-derived origin feeding DOM, URL, or eval sinks

_Area: Web injection and code execution. Seen in 21 disclosed reports; 12 confirmed bounties, median $500, range $500-$5,000; latest 2025-07-02. Updated 2026-10-23._

## How it works
A page registers a `message` listener and treats incoming data as trusted. The broken assumption is that only a legitimate opener or parent frame sends messages. Any site holding a window reference (an opener it created, or a frame it embeds) can post to the page. When the handler skips `event.origin`/`event.source` validation, derives the expected origin from attacker-controllable input (query string, referrer, the attacker's own tenant), or compares with a loose operator, the check passes for the attacker and their data reaches a sink. A second failure is at the sink: even a correctly validated sender can pass a `javascript:` URL into a location or form-action assignment.

## Where it shows up
- `addEventListener('message')` / `onmessage` in payment, checkout, dialog, chat, IDE/webview bundles.
- Origin check read from a URL parameter (`parentOrigin`, `origin`, `lockOrigin`) or compared against `document.referrer`.
- Origin compared to a value the attacker controls (their own shop/tenant) or with substring/prefix matching, so a lookalike origin passes.
- Framework event wrappers that copy only whitelisted properties, dropping `origin` (SproutCore `SC.Event`), so validation reads `undefined`.
- Message fields flowing into `innerHTML`, `form.action`, `location`, `replaceRoute`, `window.open`, `new Function`, `eval`, or RPC dispatch tables.
- Handlers that proxy to third-party SDKs or JSONP loaders.

## How to review
Find every message listener and confirm the invariant: the handler validates `event.origin` against a hardcoded allowlist using exact equality AND checks `event.source` before any data is used. `event.source` alone is insufficient when origin is spoofable via referrer or query [E1](https://bugbountylibrary.com/r/h1%3A422043.md)[E2](https://bugbountylibrary.com/r/h1%3A900619.md)[E8](https://bugbountylibrary.com/r/blog%3Akeerok-github-io-2021-05-09-xss-via-postmessage-in-chat-mozilla-org-cve-2021-21320.md). Verify the origin value is not sourced from query, referrer, or a wrapped event object missing `origin`; for SproutCore, confirm the wrapper preserves `origin`, since `URI(undefined)` defaults to the current page and every message validates [E5](https://bugbountylibrary.com/r/blog%3Aweb-archive-org-web-20211016075506-https-insight-claranet-co-uk-technical-blogs-hunting-postmessage-vulnerabilities.md). Check the comparison operator: substring/prefix matches let lookalike origins through. Then trace each message field to its sink and confirm scheme/URL validation, not just origin: a `javascript:` URL in `form.action` or `location` executes even from a trusted sender [E1](https://bugbountylibrary.com/r/h1%3A422043.md)[E6](https://bugbountylibrary.com/r/bc%3A0af3ca65-8e06-4323-ac91-82665ad805f4.md). For RPC-style handlers, confirm the method table cannot be driven to fetch or execute attacker code [E3](https://bugbountylibrary.com/r/blog%3Asudistark-github-io-2025-07-02-idx-html.md).

## Variants seen
- No origin check at all; payload injected directly [E7](https://bugbountylibrary.com/r/blog%3Aspaceraccoon-dev-solving-dom-xss-puzzles.md).
- Origin validated against referrer, satisfiable by attacker-opened window [E2](https://bugbountylibrary.com/r/h1%3A900619.md).
- Origin read from query parameter, turning iframe into open RPC proxy [E3](https://bugbountylibrary.com/r/blog%3Asudistark-github-io-2025-07-02-idx-html.md)[E8](https://bugbountylibrary.com/r/blog%3Akeerok-github-io-2021-05-09-xss-via-postmessage-in-chat-mozilla-org-cve-2021-21320.md).
- Origin validated against attacker-controlled tenant/shop [E1](https://bugbountylibrary.com/r/h1%3A422043.md).
- Listener dispatches to app methods that build JSONP/URLs; chain also involves JSONP and CORS, not postMessage alone [E4](https://bugbountylibrary.com/r/blog%3Ajub0bs-com-posts-2023-05-05-smorgasbord-of-a-bug-chain.md).
- Framework wrapper drops `origin`, so check always passes [E5](https://bugbountylibrary.com/r/blog%3Aweb-archive-org-web-20211016075506-https-insight-claranet-co-uk-technical-blogs-hunting-postmessage-vulnerabilities.md).
- Trusted sender passes `javascript:` URL into form action/location sink [E1](https://bugbountylibrary.com/r/h1%3A422043.md)[E6](https://bugbountylibrary.com/r/bc%3A0af3ca65-8e06-4323-ac91-82665ad805f4.md).

## Impact and severity
Led to DOM XSS on payment and checkout pages [E1](https://bugbountylibrary.com/r/h1%3A422043.md)[E6](https://bugbountylibrary.com/r/bc%3A0af3ca65-8e06-4323-ac91-82665ad805f4.md), token theft [E2](https://bugbountylibrary.com/r/h1%3A900619.md), code execution in the origin context [E3](https://bugbountylibrary.com/r/blog%3Asudistark-github-io-2025-07-02-idx-html.md), and cookie read/write via SDK proxy [E6](https://bugbountylibrary.com/r/bc%3A0af3ca65-8e06-4323-ac91-82665ad805f4.md). Severity rises when the origin holds auth tokens, metadata-service access, or payment flows.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A422043.md
- E2: https://bugbountylibrary.com/r/h1%3A900619.md
- E3: https://bugbountylibrary.com/r/blog%3Asudistark-github-io-2025-07-02-idx-html.md
- E4: https://bugbountylibrary.com/r/blog%3Ajub0bs-com-posts-2023-05-05-smorgasbord-of-a-bug-chain.md
- E5: https://bugbountylibrary.com/r/blog%3Aweb-archive-org-web-20211016075506-https-insight-claranet-co-uk-technical-blogs-hunting-postmessage-vulnerabilities.md
- E6: https://bugbountylibrary.com/r/bc%3A0af3ca65-8e06-4323-ac91-82665ad805f4.md
- E7: https://bugbountylibrary.com/r/blog%3Aspaceraccoon-dev-solving-dom-xss-puzzles.md
- E8: https://bugbountylibrary.com/r/blog%3Akeerok-github-io-2021-05-09-xss-via-postmessage-in-chat-mozilla-org-cve-2021-21320.md
