targetOrigin写成*会导致消息被任意源接收,引发信息泄露、XSS或CSRF攻击;CDN/代理导致实际origin偏移时也难以排查。应严格指定协议、域名、端口完整的origin。

postMessage 发送消息时 targetOrigin 写成 * 会出什么问题
写成 * 表示允许消息发给任意源,看似方便,实际等于放弃来源校验。一旦 iframe 被恶意替换(比如中间人劫持、URL 被篡改),父页面发的敏感数据(如 token、用户 ID)就可能被任意站点截获。
更隐蔽的问题是:某些企业级 CDN 或代理网关会重写响应头或注入脚本,导致子页面实际加载的 origin 和你预期不一致,* 会让这类异常完全静默通过,调试时很难定位。
实操建议:
- 始终用具体 origin,例如
'https://upload.example.com:8088',协议、域名、端口缺一不可 - 如果子页面有多个部署环境(dev/staging/prod),用配置驱动 origin,别硬编码
- 开发期可临时用
*快速验证流程,但上线前必须改掉
iframe.contentWindow 为空或报错 “Blocked a frame with origin”
常见错误现象是调用 iFrame.contentWindow.postMessage(...) 时抛出 TypeError: Cannot read property 'postMessage' of null,或控制台显示 Blocked a frame with origin ... from accessing a cross-origin frame。
立即学习“前端免费学习笔记(深入)”;
根本原因不是跨域本身,而是你试图在 iframe 还没加载完成、或加载失败(404/500)、或被浏览器策略阻止(如 X-Frame-Options)时就访问 contentWindow。
实操建议:
- 监听
iframe.onload事件,而不是 DOM ready 后立刻操作 - 加一层存在性判断:
if (iFrame.contentWindow && iFrame.contentWindow.postMessage) - 服务端确保子页面返回
X-Frame-Options: ALLOW-FROM https://parent.example.com或Content-Security-Policy: frame-ancestors https://parent.example.com
message 事件里如何安全校验 event.origin
只检查 event.origin 是否等于预期值还不够。真实环境中,子页面可能被嵌套多层(比如 A → B → C),而 event.origin 返回的是直接发送方的源,不是最初发起方的源。
更关键的是:攻击者可以伪造 postMessage 调用(比如在控制台手动执行),所以仅靠 origin 校验不能代替业务层鉴权。
实操建议:
- 用严格相等(
===)比对event.origin,避免正则或 substring 匹配引入绕过 - 如果子页面是自己可控的,让子页面在首次通信时附带一个一次性 nonce,父页面存起来,后续每条消息都要求携带该 nonce 并校验
- 对敏感操作(如支付回调、权限变更)额外要求签名字段,由后端密钥验签
传对象时 data 为空或变成 {}
现象是子页面收到的 event.data 是空对象或 null,但父页面明明传了完整对象。这通常不是 postMessage 本身的问题,而是结构化克隆失败。
postMessage 底层用的是结构化克隆算法(structured clone algorithm),它不支持函数、undefined、Symbol、DOM 节点、循环引用对象等。遇到不支持的类型会静默丢弃或转为空值。
实操建议:
- 发送前用
JSON.stringify()+JSON.parse()做一次清洗,强制只传纯数据 - 避免传
Date、RegExp等内置对象,统一转成字符串再解析 - 如果必须传 ArrayBuffer 或 Blob,显式使用
transfer参数,否则它们会被拷贝而非转移,大文件时性能极差
跨域通信真正难的不是调通第一句 postMessage,而是让每一条消息在各种异常路径下(网络中断、页面刷新、iframe 重载、CDN 缓存污染)都能被正确识别、拒绝或重试。origin 校验和数据序列化这两步,漏掉任何一个,上线后都可能变成线上事故的导火索。



















