← 返回题目列表

postMessage 有哪些安全风险?前端如何正确校验?

高频 中等 第 12 / 26 题 更新于 2026/07/29
postMessageiframe跨窗口通信前端安全

简化版

postMessage 用于跨窗口、iframe、Worker 等场景通信,安全关键是发送时指定精确 targetOrigin,接收时校验 event.originevent.source 和消息结构。不要使用 * 发送敏感数据,也不要把收到的数据直接写入 DOM 或当作命令执行。

详细版

安全接收示例:

const allowedOrigins = new Set(['https://pay.example.com'])

window.addEventListener('message', event => {
  if (!allowedOrigins.has(event.origin)) return
  if (event.source !== document.querySelector('#pay-frame').contentWindow) return
  if (!event.data || event.data.type !== 'PAY_RESULT') return

  renderPayResult(event.data.payload)
})

常见漏洞是接收端不校验来源、发送端使用 targetOrigin='*'、消息类型没有白名单、把消息内容拼进 innerHTML。面试要把它和同源策略联系起来:postMessage 是浏览器提供的受控跨源通道,安全性取决于你是否把通道边界验证好。

完整版教学

一、postMessage 为什么需要安全校验

同源策略限制一个页面直接读取另一个源的 DOM,但业务又经常需要跨源 iframe 通信,例如支付 iframe、单点登录弹窗、嵌入式客服和第三方报表。postMessage 就是浏览器给出的显式通信通道。

main.example.com
  └─ iframe: pay.example.com
       -> window.parent.postMessage({ type: 'PAY_RESULT' }, 'https://main.example.com')

记忆钩子:postMessage 打开了一扇门,targetOriginevent.origin 就是门牌号和验票口。

二、发送端要写精确 targetOrigin

iframe.contentWindow.postMessage(
  { type: 'INIT', orderId: 'A001' },
  'https://pay.example.com',
)

第二个参数不是装饰,它决定消息允许发给哪个 origin。如果写成 *,当 iframe 被导航到恶意站点时,敏感消息仍可能发过去。假设支付 iframe 原本是 pay.example.com,后来被重定向到 evil.comtargetOrigin='*' 会让订单信息落到攻击者窗口。

只有在消息完全不敏感、且接收方确实不固定时,才考虑 *。即使如此,接收方也必须严格校验。

三、接收端校验三件事

校验项作用常见错误
event.origin确认消息来自哪个源includes('example.com') 模糊匹配
event.source确认来自哪个窗口对象只校验 origin,不管窗口来源
event.data确认消息结构和类型任意字段都信任
function isPayMessage(data) {
  return data &&
    data.type === 'PAY_RESULT' &&
    typeof data.payload?.orderId === 'string' &&
    ['success', 'failed'].includes(data.payload.status)
}

origin 应使用精确白名单,不要用字符串包含判断。https://example.com.evil.com 也包含 example.com,但它不是你的可信源。

四、消息数据不能直接进入危险 API

window.addEventListener('message', event => {
  if (event.origin !== 'https://trusted.example') return
  box.innerHTML = event.data.html // 危险:仍可能变成 DOM XSS
})

来源可信不等于内容永远安全。可信系统也可能被配置错误、被 XSS 污染或传回用户可控内容。收到消息后仍要按数据类型处理:文本用 textContent,HTML 需要可靠净化,命令类消息必须走白名单。

五、设计消息协议

一个稳妥的消息协议至少包括类型、版本、请求 id 和载荷:

{
  type: 'PAY_RESULT',
  version: 1,
  requestId: 'req-001',
  payload: { orderId: 'A001', status: 'success' }
}

这样做的好处是可以防止不同业务消息混用,也方便超时、重试和回包匹配。若一次页面里有 3 个 iframe,同一个 PAY_RESULT 必须能对应到正确订单,否则就可能把 A 用户的状态渲染到 B 订单上。

六、与 iframe 沙箱配合

iframe sandbox 可以限制嵌入页面的能力,例如脚本、表单、弹窗、同源权限等。postMessage 负责通信,sandbox 负责降低被嵌入内容失控后的影响面。

配置含义
sandbox默认开启多种限制
allow-scripts允许脚本执行
allow-forms允许表单提交
allow-same-origin保留原 origin,需谨慎

如果给第三方 iframe 同时加 allow-scriptsallow-same-origin,要特别小心,因为它可能重新获得较强能力。前端安全不是只靠一个 API,而是组合边界。

七、常见误区与追问

  • 误区:postMessage 天然安全。 它只是提供跨源通信能力,安全取决于发送和接收校验。
  • 误区:targetOrigin='*' 没问题。 发送敏感数据时应指定精确 origin,避免窗口被导航后泄露。
  • 误区:校验 origin.includes() 就够了。 字符串包含会被伪造域名绕过,应使用精确白名单。
  • 追问:为什么还要校验 event.source 同一个可信 origin 可能有多个窗口,source 能进一步绑定预期窗口。
  • 追问:收到可信源的 HTML 能直接渲染吗? 不能,仍要防 DOM XSS 和上游污染。
  • 追问:如何处理响应对应关系? 使用 requestId、超时和类型白名单设计消息协议。

八、加强记忆

postMessage 安全答题可以按“发准、收严、用稳”记:发送指定精确 targetOrigin,接收校验 origin/source/data,使用时按白名单和安全 DOM API 处理。它不是同源策略的漏洞,而是浏览器提供的受控跨源通道;控制不严,通道就会变成攻击入口。