简体   繁体   English

CSP没有跨域iframe的不安全内联寄存器加载处理程序

[英]CSP without unsafe-inline register onload handler for cross-origin iframe

our motivation 我们的动力

We're implementing CSP 2.0 with script-src: 'self' (especially no 'unsafe-inline' ). 我们正在使用script-src: 'self' (尤其是'unsafe-inline' )实现CSP 2.0。 In some places we render iframes with cross-origin content and we need to know when they are loaded. 在某些地方,我们用跨域内容渲染iframe,我们需要知道它们何时加载。 Before CSP we would simply write 在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>

However, without 'unsafe-inline' we can't use the onload="this.complete = true" any more. 但是,如果没有'unsafe-inline'我们将无法再使用onload="this.complete = true" Simply attaching the event listener does not work if it did already fire: 如果事件监听器已经触发,仅附加事件监听器将不起作用:

<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>

what we tried 我们尝试了什么

Now, a solution would be to look inside the iframe contents document.readystate , but with cross-origin content we get security exceptions here. 现在,一种解决方案是查看iframe内容document.readystate ,但是对于跨域内容,我们在这里遇到了安全异常。

Nonces don't work on inline handlers (at least in CSP 2.0), so simply supplying a nonce for the inline handler is not an option. 随机数在内联处理程序上不起作用(至少在CSP 2.0中),因此不能为内联处理程序提供随机数。

Our final idea would be to rewrite all onload iframes to javascript-inserted elements, because there we could attach the load handler before inserting the element, like 我们的最终想法是将所有onload iframe重写为javascript插入的元素,因为在插入元素之前,我们可以在其中附加加载处理程序,例如

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

But for this approach we fear the performance impact of creating iframes via JavaScript -- the html iframe would be rendered and loaded before the script would even start to execute. 但是对于这种方法,我们担心通过JavaScript创建iframe会对性能产生影响-html iframe会在脚本甚至开始执行之前就呈现并加载。

our question 我们的问题

How do we figure out reliably if the content of a cross-origin iframe is loaded without an onload="this.complete = true" inline handler (that would need CSP 2.0's script-src: ... 'unsafe-inline' )? 我们如何可靠地确定是否在没有onload="this.complete = true"内联处理程序的情况下加载跨源iframe的内容(这将需要CSP 2.0的script-src: ... 'unsafe-inline' )?

Without any active cooperation from those 3rd-party providers (like postMessage communication), I think inserting those iframes via JS is your only realistic option. 没有那些第三方供应商的任何积极合作(例如postMessage通信),我认为通过JS插入这些iframe是您唯一可行的选择。 Or to at least “trigger” them to load their actually content only after you had a chance to add your load handler - so you could keep <iframe src="about:blank" data-real-src="http://3rd.party/..."> in your HTML, and have a script coming after that switch out src for data-real-src after adding the load handler. 或者至少在有机会添加加载处理程序之后至少“触发”它们以加载其实际内容-这样您就可以保留<iframe src="about:blank" data-real-src="http://3rd.party/..."> HTML中的<iframe src="about:blank" data-real-src="http://3rd.party/..."> ,并在添加加载处理程序后,在该脚本之后将src切换为data-real-src But those inline scripts will be “render blocking”, so not the best regarding overall page performance. 但是那些内联脚本将是“渲染阻止”,因此对于整体页面性能而言并不是最好的。

Maybe you'd have more success by loading a minimal document from your own domain into the iframes first, that does two things: Reach up into the parent document and attach the load event handler, and then redirect itself to the actual 3rd-party target URL afterwards …? 也许您会通过首先从自己的域中将最小的文档加载到iframe中来获得更大的成功,这会做两件事:到达父文档并附加加载事件处理程序,然后将自身重定向到实际的第三方目标之后的网址...?

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

Passing the iframe element id as a GET parameter would allow you to directly locate the corresponding iframe element in the parent document (by creating that part of the JS code dynamically, server-side), 通过将iframe元素ID作为GET参数传递,您可以直接在父文档中找到相应的iframe元素(通过在服务器端动态创建JS代码的这一部分),

parent.document.querySelector('iframe123').addEventlistener(...);
location.href = 'http://3rd.party/...';

The iframe element should be accessible in the parent document at this point already, because if it wasn't ... our script inside the iframe would hardly have loaded yet to begin with. 此时,iframe元素应该已经可以在父文档中访问了,因为如果不是这样,则iframe中的脚本几乎不会被加载。

This still would not eliminate all delay, but I think this could be faster (no render blocking), and cleaner than having inline scripts scattered all over the parent document. 这仍然不能消除所有延迟,但是与将内联脚本分散在整个父文档中相比,这样做可能更快(没有渲染阻塞)并且更干净。

(Plus, you could implement a non-JS fallback that would at least still load the iframe contents, if you add a meta refresh to those loader documents that would redirect to the target URL with a small delay ... if even applicable.) (此外,如果您向那些加载器文档添加元刷新,并以较小的延迟重定向到目标网址,则可以实施非JS后备广告,该更新至少仍会加载iframe内容...)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM