FumadocsZDecode
浏览器

Iframe 通信

跨源的父页面与 iframe 之间无法直接读取对方的 DOM 或变量,需要通过 window.postMessage 传递消息。

postMessage API

targetWindow.postMessage(data, targetOrigin)
参数说明
data发送的数据,会被结构化克隆,支持对象、数组等
targetOrigin接收方的源(协议 + 域名 + 端口),指定 '*' 表示不限来源

接收方通过监听 message 事件获取消息:

事件属性说明
event.data接收到的数据
event.origin发送方的源
event.source发送方的 window 对象引用,可用于回复

始终校验 event.origin,忽略来源不可信的消息,否则页面上任意第三方 iframe 都可以向你发消息。

父页面 → iframe

// 父页面:向 iframe 发送消息
const iframe = document.querySelector('iframe')
iframe.contentWindow.postMessage(
  { type: 'greeting', text: 'hello' },
  'https://child.example.com',  // iframe 的源
)
// iframe:接收父页面消息
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://parent.example.com') return  // 校验来源

  console.log(event.data)  // { type: 'greeting', text: 'hello' }
})

iframe → 父页面

// iframe:向父页面发送消息
window.parent.postMessage(
  { type: 'response', text: 'hello parent' },
  'https://parent.example.com',  // 父页面的源
)
// 父页面:接收 iframe 消息
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://child.example.com') return  // 校验来源

  console.log(event.data)  // { type: 'response', text: 'hello parent' }
})

利用 event.source 回复消息

接收到消息后,可以通过 event.source 直接向发送方回复,无需提前持有对方的 window 引用:

// iframe:收到消息后回复父页面
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://parent.example.com') return

  // event.source 是父页面的 window
  event.source.postMessage(
    { type: 'ack', received: true },
    event.origin,  // 用 event.origin 作为 targetOrigin,安全且准确
  )
})

安全注意事项

不要使用 targetOrigin: '*',除非明确不在乎消息被任何页面收到。如果页面被恶意 iframe 嵌套,* 会导致敏感数据泄露给攻击者。

// ❌ 不安全:任何页面都能收到消息
iframe.contentWindow.postMessage(sensitiveData, '*')

// ✅ 安全:只有指定源的页面才能收到
iframe.contentWindow.postMessage(sensitiveData, 'https://child.example.com')

接收方同样需要过滤来源:

window.addEventListener('message', (event) => {
  // ❌ 不校验来源,任何页面发来的消息都会处理
  handleMessage(event.data)

  // ✅ 只处理可信来源的消息
  if (event.origin !== 'https://trusted.example.com') return
  handleMessage(event.data)
})

On this page