Find the DOM XSS in the postMessage handler
This handler receives cross-window postMessage events and navigates on request. Find the client-side (DOM) XSS and describe the fix.
Constraints:
- the handler trusts any sender — no
event.origincheck urlcomes straight from the message payload
function handleMessage(e) {
const data = JSON.parse(e.data);
if (data.type === "go_to_link") {
window.location.assign(data.url);
}
}
window.addEventListener("message", handleMessage);
Find and fix the bug.
DOM XSS: any window can post {type:'go_to_link', url:'javascript:alert(1)'} and the handler runs it via location.assign, executing script. Two flaws: no event.origin allowlist and no URL-scheme validation. Fix: check e.origin against a trusted list, then accept only http/https URLs (reject javascript:/data:) before navigating.
- ✗Mistaking DOM XSS for DoS, CSRF, or missing output escaping
- ✗Thinking
location.assigndoes not execute thejavascript:scheme - ✗Fixing only one of the two problems (origin OR URL scheme)
- →Why is an
event.origincheck insufficient without validating the URL scheme? - →How does DOM XSS differ from reflected XSS in where it executes?
The vulnerability
The handler accepts a message from any window (no e.origin check) and navigates to the url from the payload:
window.location.assign(data.url);
location.assign("javascript:alert(1)") runs JS in the page context. An attacker window posts {type:'go_to_link', url:'javascript:alert(document.cookie)'} — this is DOM XSS.
The fix
const TRUSTED = ["https://app.example.com"];
function handleMessage(e) {
if (!TRUSTED.includes(e.origin)) return;
const data = JSON.parse(e.data);
if (data.type === "go_to_link") {
const u = new URL(data.url, location.href);
if (u.protocol === "http:" || u.protocol === "https:") {
window.location.assign(u.href);
}
}
}
✅ Validate both e.origin against an allowlist AND the URL scheme. ⚠️ Either check alone is insufficient.