javascript - postMessage 到沙盒 iframe,为啥收件人窗口原点为空?

Posted

技术标签:

【中文标题】javascript - postMessage 到沙盒 iframe,为啥收件人窗口原点为空?【英文标题】:javascript - postMessage to sandboxed iframe, why is recipient window origin null?javascript - postMessage 到沙盒 iframe,为什么收件人窗口原点为空? 【发布时间】:2017-11-17 20:33:19 【问题描述】:

测试中的 2 个 postMessage 调用:1 个使用星号作为 targetOrigin,一个使用父文档和子文档的相同 https url。

按钮 1:

$('.iframed')[0].contentWindow.postMessage( messageData , '*' );

按钮 2:

$('.iframed')[0].contentWindow.postMessage( messageData , 'https://myurl.net' );

html文档中的iframe元素,指向同域同目录下的子html文件:

<iframe name="childFrame" class="iframed" src="child.html" sandbox="allow-scripts"></iframe>

在我单击按钮触发 postMessage 之前,两个文档都已完全加载。

============================================

使用如上编写的 iframe 元素,按钮 1 对子 iframe 执行 postMessage 并成功触发子的 postMessage 侦听器(尽管它使用星号作为 targetOrigin,我不想这样做。)但是,按钮 2 结果在控制台中出现以下错误:

“在‘DOMWindow’上执行‘postMessage’失败:目标原点 提供的 ('https://myurl.net') 与收件人窗口的不匹配 原点(‘null’)。”

============================================

如果我在 iframe 的沙箱参数中添加“allow-same-origin”,则两个按钮都会成功传递 postMessage 数据(按钮 2 postMessage 调用上没有“null”错误,并且为 targetOrigin 提供了 url。)但是,我不想这样做,因为我正在使用 iframe 的沙盒行为来阻止 iframe 内容调用父文档中的 js 函数。这是一个允许将“任意”内容(html/js/images/pdfs——尽管没有像 php 这样的服务器可执行文件)加载到子 iframe 中的系统。

也许需要注意的是,iframe 内容中的类似按钮可以将 postMessage 发送到父文档,无论允许相同的来源参数或星号/url 的存在如何:

我为按钮 1 加框:

parent.postMessage( messageData , 'https://myurl.net' ); 

iframed 按钮 2:

parent.postMessage( messageData , '*' ); 

============================================

那么,如果我不添加“allow-same-origin”,为什么从父级到 iframe 的 postMessage 会导致上述错误(为什么这个问题不会影响到父级的 iframe postMessage)?我尝试将 iframe src 设置为 child.html 文档的绝对 https url,但结果是相同的。我还在不同的非 ssl-cert 服务器位置测试了相同的代码,并且得到了相同的结果(所以不要认为它是 https 贡献的......)。我必须将星号作为 targetOrigin,和/或在沙箱参数中使用 allow-same-origin 吗?

关于 SO 上这个问题的其他对话似乎是死胡同,因此希望对解决方案有新的看法......

【问题讨论】:

你解决了这个问题吗?我遇到了完全相同的问题。 嗨雪人。你解决了这个问题吗? (:已经有一段时间了。 【参考方案1】:

问题是由&lt;iframe&gt; 本身造成的,它的sandbox 属性:

<iframe name="childFrame" class="iframed" src="child.html" sandbox="allow-scripts"></iframe>

根据 Mozilla 开发者网络文档on this element,以下给出了关于同源策略的问题:

allow-same-origin:如果不使用此令牌,则资源将被视为来自始终未通过同源策略的特殊来源。

您没有指定allow-same-origin,这意味着该帧被视为来自特殊来源,并且对该帧的postMessage 调用将失败。

要解决这个问题,只需将allow-same-origin 添加到sandbox 属性中,如下所示:

<!-- index.html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<iframe name="childFrame" class="iframed" src="child.html" sandbox="allow-scripts allow-same-origin"></iframe>
<button class="btn1">A</button>
<button class="btn2">B</button>

<script>
$('.btn1').click(function(event) 
    // This works!
    $('.iframed')[0].contentWindow.postMessage( "something" , '*' );
);
$('.btn2').click(function(event) 
    // This will work too
    $('.iframed')[0].contentWindow.postMessage( "whatever you want" , 'https://myurl.net' );
);
</script>
<!-- child.html -->
<script type="text/javascript">
    window.onmessage = function(event) 
        console.log(event.data);
    
</script>

就是这样!

【讨论】:

以上是关于javascript - postMessage 到沙盒 iframe,为啥收件人窗口原点为空?的主要内容,如果未能解决你的问题,请参考以下文章

javascript postMessage - 测试消息的接收

javascript 滚动扩展 - postMessage - Stroeer

保护 Javascript 同源 postMessage 调用和消息监听器,这就够了吗?

HTML5 postMessage 和 onmessage API 具体应用

解决Iframe跨域高度自适应,利用window.postMessage()实现跨域消息传递页面高度(JavaScript)

javascript 提供用于从React-Native WebView发送和接收消息的示例实现(使用postMessage / onMessage WebVie)