假设你不希望其他网站在<iframe>中“框架”你的网站:

<iframe src="http://example.org"></iframe>

所以你在所有页面中插入反帧,帧分解JavaScript:

/* break us out of any containing iframes */
if (top != self) { top.location.replace(self.location.href); }

太好了!现在,您可以自动“打破”或跳出任何包含iframe的框架。除了一个小问题。

事实证明,你的框架破坏代码可以被破坏,如下所示:

<script type="text/javascript">
    var prevent_bust = 0  
    window.onbeforeunload = function() { prevent_bust++ }  
    setInterval(function() {  
      if (prevent_bust > 0) {  
        prevent_bust -= 2  
        window.top.location = 'http://example.org/page-which-responds-with-204'  
      }  
    }, 1)  
</script>

这段代码执行以下操作:

每次浏览器试图通过窗口导航离开当前页面时,递增一个计数器。Onbeforeunload事件处理程序 设置一个计时器,通过setInterval()每毫秒触发一次,如果它看到计数器增加,则将当前位置更改为攻击者控制的服务器 该服务器提供一个HTTP状态码为204的页面,这不会导致浏览器在任何地方导航

我的问题是——这更像是一个JavaScript难题,而不是一个实际问题——如何打败框架破坏破坏者?

我有一些想法,但在我的测试中没有什么管用:

试图通过onbeforeunload = null清除onbeforeunload事件没有效果 添加alert()会停止进程,让用户知道它正在发生,但不会以任何方式干扰代码;单击OK,让破坏照常进行 我想不出任何方法来清除setInterval()定时器

我不是一个很好的JavaScript程序员,所以这是我对你的挑战:嘿,老兄,你能打破框架破坏老兄吗?


当前回答

我将勇敢地把我的帽子投到这个戒指上(尽管它很古老),看看我能收集到多少反对票。

以下是我的尝试,在我测试的所有地方(Chrome20, IE8和FF14),它似乎都有效:

(function() {
    if (top == self) {
        return;
    }

    setInterval(function() {
        top.location.replace(document.location);
        setTimeout(function() {
            var xhr = new XMLHttpRequest();
            xhr.open(
                'get',
                'http://mysite.tld/page-that-takes-a-while-to-load',
                false
            );
            xhr.send(null);
        }, 0);
    }, 1);
}());

我把这段代码放在<head>,并从<body>的末尾调用它,以确保我的页面在它开始与恶意代码争论之前被渲染,不知道这是否是最好的方法,YMMV。

它是如何工作的?

...我听到你问了——诚实的回答是,我真的不知道。为了让它在我测试的所有地方都能正常工作,我花了很多功夫,而且它的确切效果会因运行位置的不同而略有不同。

这背后的想法是:

Set a function to run at the lowest possible interval. The basic concept behind any of the realistic solutions I have seen is to fill up the scheduler with more events than the frame buster-buster has. Every time the function fires, try and change the location of the top frame. Fairly obvious requirement. Also schedule a function to run immediately which will take a long time to complete (thereby blocking the frame buster-buster from interfering with the location change). I chose a synchronous XMLHttpRequest because it's the only mechanism I can think of that doesn't require (or at least ask for) user interaction and doesn't chew up the user's CPU time.

对于我的http://mysite.tld/page-that-takes-a-while-to-load (XHR的目标),我使用了一个PHP脚本,看起来像这样:

<?php sleep(5);

会发生什么呢?

Chrome和Firefox在XHR完成时等待5秒,然后成功重定向到框架页面的URL。 IE几乎立即重定向

你不能避免在Chrome和Firefox中等待时间吗?

Apparently not. At first I pointed the XHR to a URL that would return a 404 - this didn't work in Firefox. Then I tried the sleep(5); approach that I eventually landed on for this answer, then I started playing around with the sleep length in various ways. I could find no real pattern to the behaviour, but I did find that if it is too short, specifically Firefox will not play ball (Chrome and IE seem to be fairly well behaved). I don't know what the definition of "too short" is in real terms, but 5 seconds seems to work every time.


如果任何路过的Javascript高手想要更好地解释一下发生了什么,为什么这(可能)是错误的,不可靠的,为什么这是他们见过的最糟糕的代码等等,我很乐意听。

其他回答

我将勇敢地把我的帽子投到这个戒指上(尽管它很古老),看看我能收集到多少反对票。

以下是我的尝试,在我测试的所有地方(Chrome20, IE8和FF14),它似乎都有效:

(function() {
    if (top == self) {
        return;
    }

    setInterval(function() {
        top.location.replace(document.location);
        setTimeout(function() {
            var xhr = new XMLHttpRequest();
            xhr.open(
                'get',
                'http://mysite.tld/page-that-takes-a-while-to-load',
                false
            );
            xhr.send(null);
        }, 0);
    }, 1);
}());

我把这段代码放在<head>,并从<body>的末尾调用它,以确保我的页面在它开始与恶意代码争论之前被渲染,不知道这是否是最好的方法,YMMV。

它是如何工作的?

...我听到你问了——诚实的回答是,我真的不知道。为了让它在我测试的所有地方都能正常工作,我花了很多功夫,而且它的确切效果会因运行位置的不同而略有不同。

这背后的想法是:

Set a function to run at the lowest possible interval. The basic concept behind any of the realistic solutions I have seen is to fill up the scheduler with more events than the frame buster-buster has. Every time the function fires, try and change the location of the top frame. Fairly obvious requirement. Also schedule a function to run immediately which will take a long time to complete (thereby blocking the frame buster-buster from interfering with the location change). I chose a synchronous XMLHttpRequest because it's the only mechanism I can think of that doesn't require (or at least ask for) user interaction and doesn't chew up the user's CPU time.

对于我的http://mysite.tld/page-that-takes-a-while-to-load (XHR的目标),我使用了一个PHP脚本,看起来像这样:

<?php sleep(5);

会发生什么呢?

Chrome和Firefox在XHR完成时等待5秒,然后成功重定向到框架页面的URL。 IE几乎立即重定向

你不能避免在Chrome和Firefox中等待时间吗?

Apparently not. At first I pointed the XHR to a URL that would return a 404 - this didn't work in Firefox. Then I tried the sleep(5); approach that I eventually landed on for this answer, then I started playing around with the sleep length in various ways. I could find no real pattern to the behaviour, but I did find that if it is too short, specifically Firefox will not play ball (Chrome and IE seem to be fairly well behaved). I don't know what the definition of "too short" is in real terms, but 5 seconds seems to work every time.


如果任何路过的Javascript高手想要更好地解释一下发生了什么,为什么这(可能)是错误的,不可靠的,为什么这是他们见过的最糟糕的代码等等,我很乐意听。

我想到了这个,它至少在Firefox和Opera浏览器中是有效的。

if(top != self) {
 top.onbeforeunload = function() {};
 top.location.replace(self.location.href);
}

所有建议的解决方案都直接强制改变顶部窗口的位置。如果用户想要帧在那里怎么办?例如,搜索引擎的图像结果中的顶部帧。

我写了一个原型,默认情况下所有输入(链接、表单和输入元素)都是禁用的,激活时什么都不做。

如果检测到包含帧,输入将被禁用,并在页面顶部显示警告消息。警告消息包含一个链接,该链接将在新窗口中打开该页的安全版本。这可以防止页面被用于点击劫持,同时仍然允许用户在其他情况下查看内容。

如果没有检测到包含帧,则输入被启用。

这是代码。您需要将标准HTML属性设置为安全值,并添加包含实际值的附加属性。它可能是不完整的,对于完全安全的附加属性(我在考虑事件处理程序)可能必须以同样的方式处理:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
  <head>
    <title></title>
    <script><!--
      function replaceAttributeValuesWithActualOnes( array, attributeName, actualValueAttributeName, additionalProcessor ) {
        for ( var elementIndex = 0; elementIndex < array.length; elementIndex += 1 ) {
          var element = array[ elementIndex ];
          var actualValue = element.getAttribute( actualValueAttributeName );
          if ( actualValue != null ) {
            element[ attributeName ] = actualValue;
          }

          if ( additionalProcessor != null ) {
            additionalProcessor( element );
          }
        }
      }

      function detectFraming() {
        if ( top != self ) {
          document.getElementById( "framingWarning" ).style.display = "block";
        } else {
          replaceAttributeValuesWithActualOnes( document.links, "href", "acme:href" );

          replaceAttributeValuesWithActualOnes( document.forms, "action", "acme:action", function ( form ) {
            replaceAttributeValuesWithActualOnes( form.elements, "disabled", "acme:disabled" );
          });
        }
      }
      // -->
    </script>
  </head>
  <body onload="detectFraming()">
    <div id="framingWarning" style="display: none; border-style: solid; border-width: 4px; border-color: #F00; padding: 6px; background-color: #FFF; color: #F00;">
      <div>
        <b>SECURITY WARNING</b>: Acme App is displayed inside another page.
        To make sure your data is safe this page has been disabled.<br>
        <a href="framing-detection.html" target="_blank" style="color: #090">Continue working safely in a new tab/window</a>
      </div>
    </div>
    <p>
      Content. <a href="#" acme:href="javascript:window.alert( 'Action performed' );">Do something</a>
    </p>
    <form name="acmeForm" action="#" acme:action="real-action.html">
      <p>Name: <input type="text" name="name" value="" disabled="disabled" acme:disabled=""></p>
      <p><input type="submit" name="save" value="Save" disabled="disabled" acme:disabled=""></p>
    </form>
  </body>
</html>

如果您在buster代码之后添加警报,那么警报将使javascript线程停止,并让页面加载。这就是StackOverflow所做的,它破坏了我的iframes,即使当我使用框架破坏buster。它也适用于我的简单测试页面。这个功能只在windows下的Firefox 3.5和IE7中测试过。

代码:

<script type="text/javascript">
if (top != self){
  top.location.replace(self.location.href);
  alert("for security reasons bla bla bla");
}
</script>

那重复地叫假人老兄呢?这将创建一个竞争条件,但人们可能希望buster能够脱颖而出:

(function() {
    if(top !== self) {
        top.location.href = self.location.href;
        setTimeout(arguments.callee, 0);
    }
})();