event.origin必须用===严格比对,因includes等模糊匹配会被https://evil-example.com绕过;未校验即执行video.play()等于将控制权交予任意网站。

event.origin 必须用 === 严格比对,不能用 includes 或 indexOf
不校验 event.origin 就执行 video.play() 或 location.href,等于把控制权交给任意网站。攻击者只需在自己页面里嵌一个同名 iframe,就能伪造消息触发敏感操作。
常见错误是模糊匹配:if (event.origin.includes('example.com'))——这会被 https://evil-example.com 绕过。
必须写成:if (event.origin !== 'https://shop.example.com') return
- 多个合法来源(如
https://a.com和https://b.com)需显式白名单判断,例如['https://a.com', 'https://b.com'].includes(event.origin) - Safari 对跨域
iframe.contentWindow.location.origin访问更严,部分版本返回null,所以子页不能依赖缓存,每次都要现场读取 - 若父页 origin 动态变化(如多租户部署),建议由父页在首次通信时附带
sourceOrigin字段,子页将其存为可信源,避免反复依赖不可靠的event.origin
targetOrigin 填 "*" 在生产环境等于裸奔
targetOrigin 填 "*" 不会报错,但浏览器只在目标窗口当前 origin 与该值完全一致时才投递消息;不匹配就静默丢弃,连 Failed to execute 'postMessage' 错误都不会抛——调试时根本看不到失败痕迹。
本地开发可临时用 'http://localhost:3000',但上线前必须替换成真实协议+域名+端口,比如 'https://player.example.com:8080'。
- CDN 托管的子页(如
https://d34gxw3jqlasaag.cloudfront.net/player.html)也要写死完整origin,不能靠自动推导 - 子页若重定向(如从
http跳https),contentWindow.location.origin会变,所以必须在load回调里立即读取,不能提前缓存 - 动态创建的 iframe 同样要监听
load,不能依赖 DOM 插入顺序
移动端 video.play() 必须绑定用户手势,且需 Promise 链同步状态
这不是 bug,是 iOS/Android 强制策略:play() 必须由用户手势(click、touchstart)触发。通过 postMessage 发来的 { type: 'play' } 属于程序调用,99% 抛 NotAllowedError。
子页应在 video 元素上绑定一次 touchstart 或 click,在回调里调 video.play() 并 resolve 一个 Promise;后续所有 postMessage 指令(如 seek、volume)都等这个 Promise settled 后再执行。
- 父页可先发
{ type: 'check-ready' }探测,子页响应{ type: 'ready', canPlay: true },避免盲目重试 - 若用户未触发过手势,
canPlay应为false,父页需引导点击提示 - 不要在
catch里静默吞掉错误——至少console.warn('play failed:', err),否则调试时完全无迹可寻
消息结构不统一是隐性崩溃高发区
传 { cmd: 'seek', t: 120 } 这种随意字段名,半年后维护时连自己都看不懂;更糟的是,父页传 time: '120'(字符串),子页直接赋给 video.currentTime,会触发静音跳转或 NaN 异常。
统一用 type 作为动作标识:'play'、'pause'、'seek'、'volume';每个 type 对应固定字段约束,例如 seek 只认 time(number),volume 只认 value(0–1 之间的 number)。
- 收到未知
type时,应console.warn('unknown message type:', event.data.type),不响应也不抛错 - 建议加
id字段用于请求-响应匹配,避免指令乱序或重复执行 - 所有
postMessage调用建议包裹try...catch,防止目标窗口不可达时报错中断逻辑
load 后没立刻读取 contentWindow.location.origin,而是缓存了初始值;或者父页发消息时用了 "*" 却以为“反正能通”,结果线上环境因重定向或 CDN 域名变更导致静默失效。这些点不爆在控制台,只爆在线上行为异常。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











