防 iframe 劫持需三层防护:服务端设 content-security-policy frame-ancestors 响应头(非 meta 或 js 设置),html 中静态声明 sandbox(禁用 allow-top-navigation),postmessage 通信严格校验 targetorigin 和 event.origin,销毁 iframe 时手动清理上下文。

防 iframe 劫持不是加个 sandbox 或配个 frame-ancestors 就完事——它必须分三层打:服务端锁入口、加载前断执行、通信时验身份。漏掉任何一层,都可能被绕过。
服务端必须设好 frame-ancestors,<meta> 和 JS 设置全无效
浏览器只认 HTTP 响应头里的 Content-Security-Policy: frame-ancestors,前端写 <meta http-equiv="Content-Security-Policy"> 或用 JS 动态设置,一律忽略。常见失效表现:DevTools Network 面板看不到该 header,安全扫描仍报“缺失防护”。
-
frame-ancestors值必须带单引号,'none'、'self'、'https://trusted.com'才合法;写成none或"self"会导致整条 CSP 被丢弃 - 多个域名用空格分隔,不能加逗号或路径:
frame-ancestors 'self' https://a.com https://b.net - IE11 和 Safari ≤14.6 不支持
frame-ancestors,所以要同时发X-Frame-Options: DENY兜底 - Spring Boot 用户需禁用
spring.security.headers.frame-options,改用spring.security.headers.content-security-policy配置
sandbox 必须静态声明且禁用 allow-top-navigation
sandbox 属性在 HTML 标签里写死才生效,JS 动态添加(如 iframe.setAttribute('sandbox', 'allow-scripts'))完全没用。它的核心作用是加载前内核级隔离,而不是等 JS 运行后再拦截。
- 默认空值
sandbox=""已禁用脚本、表单、弹窗、跳转等全部能力;加allow-scripts只解禁 JS 执行,但onclick="doX()"这类内联事件仍静默失效 -
allow-top-navigation是高危开关,Chrome 83+ 已弃用,Firefox 默认禁用,Safari 仅在用户手势下部分放行——生产环境绝对不要开 - 若子页需弹窗(如支付回调),可保留
allow-popups,但绝不可与allow-scripts+allow-top-navigation组合 - srcdoc 内容同样受 sandbox 限制,哪怕没网络请求,
javascript:void(0)也点不动
postMessage 通信必须双向校验 targetOrigin 和 event.origin
跨域 iframe 唯一可用的通信通道是 postMessage,但它本身不带安全属性,全靠手动校验。填 "*" 或模糊匹配 includes() 等于裸奔。
- 父页发消息时,
targetOrigin必须写死完整 origin:iframe.contentWindow.postMessage(data, 'https://widget.example.com'),本地开发也不能用'*' - 子页收消息时,
event.origin必须用===严格比对:if (event.origin !== 'https://parent.example.com') return;includes('example.com')会被https://evil-example.com绕过 - 子页来自
about:blank或动态写入时,event.origin是'null',需单独判断,不能只靠includes() - Chrome 117+ 对
postMessage序列化更严,Date、RegExp、function会被丢弃,只传 plain object
iframe 销毁时必须手动清理 JS 上下文,否则内存泄漏
SPA 路由切换不会自动卸载 iframe 内的 JS,remove() 只删 DOM,contentWindow 仍在后台跑 setInterval、WebSocket、window.addEventListener('message', ...),且无法被 GC 回收。
- 销毁前必须先调
iframe.contentWindow?.close(),再移除 DOM 节点 - 若子页注册了全局
window.onmessage,父页应先postMessage({ type: 'cleanup' }),等子页自行释放资源后再close() - Vue 的
onUnmounted或 React 的useEffect cleanup不会触发子页逻辑,不能依赖它们 - 敏感操作(如登录态同步)必须附带一次性 token 或签名,不能只靠
origin校验
真正难的不是配置项怎么写,而是所有环节必须闭环:服务端 header 到位、HTML 标签写死 sandbox、通信消息结构统一、销毁流程显式接管。少一个,就可能被链式绕过。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











