用postMessage是当前唯一无需服务端配合、能安全完成iframe跨域通信的标准方案;但必须严格校验event.origin并精确指定targetOrigin,否则存在XSS风险,且需确保iframe加载完成后再发送消息。

直接结论:用 postMessage 是当前唯一无需服务端配合、能安全完成 iframe 跨域通信的标准方案;但“发出去”不等于“传得安全”,漏掉 event.origin 校验或乱设 targetOrigin 就等于把数据裸奔发给任意页面。
为什么不能直接读取 iframe.contentWindow?
浏览器同源策略硬性拦截:跨域时 iframe.contentWindow 可能返回 null,或访问其 document、location 等属性直接抛 SecurityError。这不是加载慢或时机不对,是浏览器从底层禁止——哪怕只是想调个函数、读个变量,都不行。
常见错误现象包括:
Blocked a frame with origin "https://a.com" from accessing a cross-origin frame.-
Cannot read property 'xxx' of null(实际是contentWindow被拦截后为null)
这时候别折腾 document.domain(只适用于子域)或 JSONP(不适用 iframe),直接切到 postMessage 路线。
怎么写一个不被绕过的 origin 校验?
接收方必须严格比对 event.origin,否则任何恶意页面都能伪造消息冒充合法来源。校验只认协议+域名+端口,不拼路径,不模糊匹配。
✅ 正确写法:if (event.origin !== 'https://b.com') return;
❌ 错误写法:if (!event.origin.includes('b.com')) return;(会被 https://evil-b.com 绕过)
❌ 危险写法:if (event.origin === '*')(event.origin 永远不会是 "*",这行永远为 false)
如果目标源有多个(如本地开发用 http://localhost:3000,线上用 https://prod.com):
const ALLOWED_ORIGINS = ['https://prod.com', 'http://localhost:3000']; if (!ALLOWED_ORIGINS.includes(event.origin)) return;
父页发消息给子 iframe 的关键步骤
发送前要确保 iframe 已加载完成,且避免明文塞敏感字段。
- 监听
iframe.onload或iframe.addEventListener('load', ...),不要靠setTimeout猜时机 - 发送时指定精确的
targetOrigin:iframe.contentWindow.postMessage(data, 'https://b.com') - 不要传
localStorage、cookie或原始 token;如需传递凭证,应走后端签发的短期票据,由子页自行向自己后端校验 - IE9–10 不支持对象自动序列化,若需兼容,先手动
JSON.stringify()
子页如何安全回传数据给父页?
子页调用 window.parent.postMessage(...) 时,targetOrigin 应设为父页确切 origin(如 'https://a.com'),而非 "*";父页监听 message 事件时,同样要校验 event.origin 是否在白名单中。
容易被忽略的一点:子页拿到消息后,若需响应,应通过 event.source.postMessage(...) 回传,而不是再查一遍 window.parent——因为 event.source 是浏览器确认过的真实发送方引用,更可靠。
复杂点在于双向信任链必须闭环:父页校验子页 origin,子页也得校验父页 origin;少一端,就可能被中间人注入或伪造响应。

















