没有不安全内联寄存器的CSP用于跨源iframe的CSP

时间:2018-03-26 07:10:28

标签: javascript html iframe content-security-policy

我们的动力

我们正在使用script-src: 'self'(特别是没有'unsafe-inline')实施CSP 2.0。在某些地方,我们会渲染具有跨内容内容的iframe,我们需要知道它们何时被加载。在CSP之前,我们只需编写

<iframe src="https://some.cross/origin/content" onload="this.complete = true"/>
<script>
  var iframe = document.querySelector('iframe');
  function loaded() { /* whatever happens when the iframe is loaded */ }
  if (iframe.complete) {
    loaded();
  } else {
    iframe.addEventListener('load', loaded);
  }
</script>

但是,如果没有'unsafe-inline',我们将无法再使用onload="this.complete = true"。如果它已经触发,只是附加事件监听器不起作用:

<iframe src="https://some.cross/origin/content"/>
<script>
  var iframe = document.querySelector('iframe');
  function loaded() { /* ... */ }
  // if the load event does not fire because it fired before
  // loaded() will never be executed
  iframe.addEventListener('load', loaded);
</script>

我们尝试了什么

现在,解决方案是查看iframe内容document.readystate,但是对于跨源内容,我们会在此处获得安全性异常。

Nonce不适用于内联处理程序(至少在CSP 2.0中),因此只能为内联处理程序提供一个nonce。

我们的最终想法是将所有onload iframe重写为插入javascript的元素,因为我们可以在插入元素之前附加加载处理程序,例如

<!-- no iframe tag anymore -->
<script>
  var iframe = document.createElement('iframe');
  function loaded() { /* ... */ }
  iframe.addEventListener('load', loaded);
  document.body.appendChild(iframe);
</script>

但是对于这种方法,我们担心通过JavaScript创建iframe会对性能产生影响 - 在脚本甚至开始执行之前,会呈现并加载html iframe。

我们的问题

如果在没有onload="this.complete = true"内联处理程序(需要CSP 2.0的script-src: ... 'unsafe-inline')的情况下加载跨源iframe的内容,我们如何可靠地计算出来?

1 个答案:

答案 0 :(得分:1)

如果没有来自第三方提供商的任何积极合作(如postMessage通信),我认为通过JS插入这些iframe是您唯一的现实选择。或者至少“触发”他们只有在你有机会添加你的加载处理程序之后才加载他们的实际内容 - 所以你可以在你的HTML中保留<iframe src="about:blank" data-real-src="http://3rd.party/...">,并在切换后有一个脚本{{1添加加载处理程序后添加src。但是这些内联脚本将是“渲染阻止”,因此不是最佳的整体页面性能。

也许您可以通过首先将自己域中的最小文档加载到iframe中来获得更多成功,这会做两件事:连接到父文档并附加加载事件处理程序,然后将自身重定向到实际的第3个之后的-party目标网址??

data-real-src

将iframe元素id作为GET参数传递将允许您直接在父文档中找到相应的iframe元素(通过动态创建JS代码的那部分,服务器端),

<iframe id="iframe123" src="/my-iframe-loader.xyz?iframeid=iframe123&
                            targeturl=http://3rd.party/...">

此时iframe元素应该可以在父文档中访问,因为如果不是......我们在iframe中的脚本几乎没有加载开始。

这仍然不会消除所有延迟,但我认为这可能更快(没有渲染阻止),并且比在整个父文档中散布的内联脚本更清晰。

(另外,你可以实现一个至少仍会加载iframe内容的非JS回退,如果你向这些加载器文档添加一个元刷新,这些加载文档会以一个小的延迟重定向到目标URL ...甚至适用。)