检测JavaScript是否在沙箱iframe中执行?

15

我有一个产品,它在Flash中播放视频(如果可用),如果Flash不可用,则回退到HTML5。

我无法找到一种方法来确定JavaScript是否在带有“sandbox”属性的Iframe内执行,这对我的解决方案非常必要,因为沙盒化的Iframe会禁用所有插件。沙盒化的Iframe可能只是这样:

<iframe src="http://www.cross-domain.com/" sandbox="allow-scripts">

为了确定Flash是否可用,我正在使用swfobject的方法检查navigator.plugins ["Shockwave Flash"] .description,即使在沙箱内嵌iframe中也可以设置。我可以加载swf对象,但无法播放。

要重现此问题,请访问http://jsfiddle.net/max_winderbaum/9cqkjo45/,打开Chrome检查器并单击“运行”。跨域站点上的脚本将在沙箱内嵌iframe的上下文中暂停。

根据W3规范http://dev.w3.org/html5/spec-preview/browsers.html#sandboxing-flag-set,文档应该有一个“活动沙箱标志集” JavaScript可以访问(至少这就是我阅读规范的方式)。似乎没有任何标志设置在iframe的文档上。

有人有什么想法/解决方案来检测JavaScript是否在沙箱内嵌iframe中执行吗?


但这不就是HTML5回退机制的意义所在吗? - theonlygusti
1
@theonlygusti 如果你继续阅读,问题在于我不知道何时需要回退到HTML5。从浏览器中获取的所有信息都告诉我Flash已启用,因此我加载Flash。如果我需要加载我的HTML5解决方案,则需要在JavaScript中知道是否禁用了Flash。如果您告诉我如何判断Flash是否已禁用,那么它将解决我的问题。 - Omninternet
文档插件怎么样呢?可能会告诉您与 navigator.plugins 不同的信息。后者是已安装的插件,而前者则是(据说)文档中的插件,由于被限制在沙盒中,可能会有所不同。虽然我没有检查过。 - Heretic Monkey
你能引用规范中的部分内容,说明沙盒标志应该对嵌入页面中的 JavaScript 可访问(而不是由 JS 引擎或其他浏览器部件在内部处理)吗? - Quentin
我可以建议您采用相反的方式,并且在使用iframe-sandbox时始终运行HTML 5视频。我的意思是,由于沙箱功能是HTML 5而Flash正在退出舞台,为什么要试图让它继续存在呢?...而且如果没有沙箱功能,就不会有检测/运行问题,对吗? - Asons
显示剩余2条评论
2个回答

6

我将考虑不同种类的iframe(选择适用的第一个案例):

注意:Firefox将数据URI视为同源,因此没有问题。但是,Chrome将它们视为跨域。然后,frameElement不起作用,无论iframe是否被沙箱化,document.domain都为空字符串。您可以检查location.protocol是否为"data:"字符串以检测数据URI。
一般情况下,您可以尝试类似以下的操作:
function isSandboxedIframe() {
  if (window.parent === window) return 'no-iframe';
  try { var f = window.frameElement; } catch(err) { f = null; }
  if(f === null) {
    if(document.domain !== '') return 'unkown'; // Probably 'non-sandboxed'
    if(location.protocol !== 'data:') return 'sandboxed';
    return 'unkown'; // Can be 'sandboxed' on Firefox
  }
  return f.hasAttribute('sandbox') ? 'sandboxed' : 'non-sandboxed';
}

1
这在Firefox上对我有用,但在Chrome上,似乎尝试从受沙盒限制的iframe访问window.frameElement会抛出异常。当然,捕获异常本身就是一个合理的诊断方法。 - Ilmari Karonen
有趣,W3C 表示浏览器应该抛出错误,而 WHATWG 表示应该返回 null。我会更新我的回答。 - Oriol
该死,它在Firefox上似乎也能在不同的域上很好地工作,但只是因为缓存。抱歉Chrome,我责怪你做得不好。那么这个答案就有点多余了。我会想办法改进它,否则我可能会删除它。 - Oriol
由于在跨源 iframe 上无法运行,所以对于我需要的功能来说并不是一个通用情况。 - Omninternet
我已经测试了sandblaster库,并且在所有测试中它的行为与我的代码相同,除了sandblaster不考虑数据URI,因此在Chrome上会产生误报。 - Oriol
显示剩余3条评论

4
一个名为 sandblaster 的项目可以帮助您检测是否正在运行沙盒。
沙盒首先检查自身是否被框架化,然后扫描框架元素的属性以检测有关自身的几个信息。这些包括 framedcrossOriginsandboxedsandboxAllowancesunsandboxableresandboxablesandboxable
在我们的情况下,要检测自身是否被沙盒化,它会检查框架元素是否具有 sandbox 属性。
// On below `frameEl` is the detected frame element
try {
  result.sandboxed = frameEl.hasAttribute("sandbox");
}
catch (sandboxErr) {
  result.sandboxed = null;
  if (typeof errback === "function") {
    errback(sandboxErr);
  }
}

我试图重现你的问题并测试这个解决方案是否有效,但由于安全问题,我不得不将脚本粘贴到窗口中。
<html>
    <head>
    </head>
    <body>

    <script>
        //Paste the contents of the script(https://raw.githubusercontent.com/JamesMGreene/sandblaster/master/dist/sandblaster.js) here

        var result = sandblaster.detect();
        if(result.sandboxed === true) {
            //sandboxed
        }
        debugger;
    </script>
    </body>
</html>

这里有一个演示:http://jsfiddle.net/Starx/tzmn4088/,展示了这个功能的工作原理。

你能否在这篇文章中补充一些关于沙箱技术如何实现的内容,以防将来该项目消失? - Tim B
@TimB,好的。我添加了一些我认为相关的内容。 - Starx
这对我非常有效,甚至跨域。关键在于_getEffectiveScriptOrigin()函数。基本上,如果您在一个沙盒iframe中,document.domain将为空字符串。否则它将被设置为某些内容。 - Omninternet
以下是一个示例,其中在检测到沙盒 iframe 的行上有一个调试器语句:http://jsfiddle.net/max_winderbaum/q9ca0c8g/ - Omninternet
@Omninternet,不错。 - Starx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接