Skip to main content

postMessage Receiver Without Origin Validation

A cross-document message listener trusts event.data without checking event.origin, so any page that can get a handle on the window can post into it. If the data reaches a DOM sink, it becomes DOM XSS or an open-navigation primitive.

What Vulkro detects​

POSTMSG-001 (Medium) fires on a window.addEventListener('message', ...) or window.onmessage = ... handler whose body does no origin validation. Confidence rises to High when the body also writes the data into a DOM sink. Severity stays Medium because the trusted origin is application-specific and this is a shape heuristic, not a taint proof.

  • Receiver side only; a send-side postMessage(data, '*') is covered by other detectors.
  • Only JS/TS-family files (.js, .jsx, .ts, .tsx, .mjs, .cjs, .vue, .svelte) that contain a message listener are parsed.
  • Inline handlers that reference an origin comparison are skipped. Named-function and single-expression handlers are flagged only when the whole file has no origin validation.

Non-compliant code​

window.addEventListener('message', (event) => {
document.getElementById('panel').innerHTML = event.data.html; // any origin can post
});

Compliant code​

const TRUSTED_ORIGIN = 'https://widget.example.com';
window.addEventListener('message', (event) => {
if (event.origin !== TRUSTED_ORIGIN) return;
if (typeof event.data?.text !== 'string') return;
document.getElementById('panel').textContent = event.data.text;
});

How to fix​

  • Compare event.origin with an exact expected origin before touching event.data, and return early on a mismatch.
  • Validate the payload shape too.
  • Keep the origin allowlist in a constant, not a wildcard-built regular expression.

References​

See also: Confidence model.


This page is generated by vulkro rules export <out-dir> from Vulkro's built-in detector catalogue. Edits made by hand are overwritten on the next regeneration.