postMessage
iframes and Window References
Window References
My Website
window.frames[0]
<iframe>
Embedded Page
(Isolated DOM)
Embedded Page
(Isolated DOM)
window.open("page2.com")
↔
postMessage
Page 2
Opened via window.open()
OR embedded as iframe
- iframe = web page within a web page
- iframe gets its own isolated DOM
- Window reference = an object in JS that represents an iframe or tab
- Can access iframes on a page with
window.frames[<index>]
- Can access iframes on a page with
- Get a window reference with
window.open()to open a new tab
The postMessage API
Sender / Receiver Flow
Sender
targetWindow.postMessage(
message, targetOrigin)
message, targetOrigin)
message
→
Receiver
addEventListener("message",
function(event) { ... })
function(event) { ... })
- API that allows communication between two windows/iframes
- Send a message to window reference via
postMessage(message, target-origin)- Message is typically a string or JSON object
- Receiving page has to set up listener via
addEventListeneroronmessage- Handler receives a MessageEvent with properties like
originanddata
- Handler receives a MessageEvent with properties like
Try It: Interactive postMessage Demo
Send a message from this page into the iframe below. Watch the receiver log the origin, data, and type of each message in real time.
→
postMessage
Quick presets:
Typical postMessage Handler
JavaScript
window.addEventListener('message', function(event) {
// 1. Origin check
if (event.origin !== "https://trusted.com") return;
// 2. Route by message type
if (event.data.type === "updateProfile") {
// 3. Do stuff if data.type is "updateProfile"
} else if (event.data.type === "redirect") {
// 3. Do some other stuff if data.type is "redirect"
}
})
1
Origin Check
2
Route by Type
3
Process Data
Weak Origin Checks
JavaScript Console
"https://jkbrah.com.attacker.com".includes("jkbrah.com")
true
"https://bsides.jkbrah.com.attacker.com".startsWith("https://bsides.jkbrah.")
true
- Sometimes have no origin check at all → Can send messages from any origin
- Weak origin check that can be bypassed:
- Using string operations like
.includes()or.startsWith()→ can bypass if we can register a domain that substring-matches the target domain - Regular Expressions with missing anchors or improper escaping
- Using string operations like
Key Takeaway: Look very closely at origin check implementation to see if it can be bypassed
Exercise: postMessage XSS
JavaScript
window.addEventListener('message', function(event) {
var data;
try {
data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
} catch(e) {
return;
}
if (!data || !data.type) return;
if (data.type === 'STATUS_UPDATE') {
widgetContent.innerHTML = data.content;
} else if (data.type === 'METRIC_PUSH') {
console.log('[StatusHub] Metric received:', data.metric, data.value);
} else if (data.type === 'THEME_CHANGE') {
document.body.setAttribute('data-theme', data.theme || 'dark');
}
});