代码之家  ›  专栏  ›  技术社区  ›  Jeff Atwood

框架终结者…需要终结者代码

  •  399
  • Jeff Atwood  · 技术社区  · 16 年前

    假设您不希望其他站点在 <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自动。除了一个小问题。

    事实证明, 你的帧破坏代码可以被破坏 , as shown here :

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

    此代码执行以下操作:

    • 每当浏览器试图通过 window.onbeforeunload 事件处理程序
    • 设置计时器,通过 setInterval() 如果发现计数器递增,则将当前位置更改为攻击者控制的服务器
    • 该服务器为一个页面提供HTTP状态代码 二百零四 ,这不会导致浏览器导航到任何位置

    我的问题是——这更像是一个javascript拼图,而不是一个实际的 问题 --你怎么能打败那个破坏框架的家伙?

    我有一些想法,但在我的测试中没有任何效果:

    • 正在尝试清除 onbeforeunload 事件通过 onbeforeunload = null 没有效果
    • 添加一个 alert() 已停止进程,让用户知道它正在发生,但不会以任何方式干扰代码;单击“确定”将使总线继续正常工作。
    • 我想不出任何方法来清除 集间Valk() 计时器

    我不是一个很好的javascript程序员,所以我的挑战是: 嘿,巴斯特,你能把破坏框架的巴斯特打倒吗?

    19 回复  |  直到 7 年前
        1
  •  141
  •   hugoware    16 年前

    我不确定这是否可行-但如果你不能打破框架,为什么不只是显示一个警告。例如,如果您的页面不是“Top page”,请创建一个setInterval方法,尝试打破框架。如果在3或4次尝试之后,您的页面仍然不是顶部页面-创建一个DIV元素,用一条消息和一个链接覆盖整个页面(模式框),如…

    您正在未经授权的框架窗口中查看此页-(blah blah…潜在安全问题)

    单击此链接可解决此问题

    不是最好的,但我看不出他们有什么办法可以用脚本来解决这个问题。

        3
  •  31
  •   Dungeon Hunter Klemenko    12 年前

    我们在我们的一个网站中使用了以下方法 http://seclab.stanford.edu/websec/framebusting/framebust.pdf

    <style>
     body { 
     display : none   
    }
    </style>
    <script>
    if(self == top) {
    document.getElementsByTagName("body")[0].style.display = 'block';
    }
    else{
    top.location = self.location;
    }
    </script>
    
        4
  •  29
  •   Dogelismoツ Jani Hartikainen    7 年前

    想到这个,它至少在火狐和Opera浏览器中有效。

    if(top != self) {
     top.onbeforeunload = function() {};
     top.location.replace(self.location.href);
    }
    
        5
  •  21
  •   user1646111    12 年前

    考虑到为iframe引入沙盒的当前HTML5标准,当攻击者使用沙盒时,可以禁用此页面中提供的所有帧破坏代码,因为它限制iframe如下:

    allow-forms: Allow form submissions.
    allow-popups: Allow opening popup windows.
    allow-pointer-lock: Allow access to pointer movement and pointer lock.
    allow-same-origin: Allow access to DOM objects when the iframe loaded form same origin
    allow-scripts: Allow executing scripts inside iframe
    allow-top-navigation: Allow navigation to top level window
    

    请参阅: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#attr-iframe-sandbox

    现在,假设攻击者使用以下代码在iframe中托管您的站点:

    <iframe src="URI" sandbox></iframe>
    

    然后,所有的javascript帧分解代码都将失败。

    在检查了所有帧业务代码之后,在所有情况下,只有这种防御才有效:

    <style id="antiClickjack">body{display:none !important;}</style>
    <script type="text/javascript">
       if (self === top) {
           var antiClickjack = document.getElementById("antiClickjack");
           antiClickjack.parentNode.removeChild(antiClickjack);
       } else {
           top.location = self.location;
       }
    </script>
    

    最初由 Gustav Rydstedt, Elie Bursztein, Dan Boneh, and Collin Jackson (2010)

        6
  •  19
  •   Nathan Osman    13 年前

    经过一段时间的思考,我相信这会告诉他们谁是老板…

    if(top != self) {
      window.open(location.href, '_top');
    }
    

    使用 _top 作为的目标参数 window.open() 将在同一窗口中启动它。

        7
  •  6
  •   Giorgio Maone    16 年前
    if (top != self) {
      top.location.replace(location);
      location.replace("about:blank"); // want me framed? no way!
    }
    
        8
  •  5
  •   mik    16 年前

    好吧,我们知道那是在一个框架里。所以我们把路径作为get变量定位到另一个特殊页面。我们现在向用户解释正在发生的事情,并提供一个带有target=“Top”选项的链接。它很简单,可能会工作(还没有测试过),但它需要一些用户交互。也许你可以向用户指出有问题的网站,让你的网站某个地方的点击Jackers感到羞耻。只是一个主意,但它是夜间工作。

        9
  •  5
  •   Johan Stuyts    16 年前

    所有建议的解决方案都直接强制更改顶部窗口的位置。如果用户希望框架在那里呢?例如,搜索引擎图像结果中的顶框。

    我编写了一个原型,默认情况下,所有输入(链接、表单和输入元素)都被禁用,并且/或者在激活时什么也不做。

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

    如果未检测到包含帧,则启用输入。

    这是密码。您需要将标准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>
    
        10
  •  5
  •   DaveRandom    11 年前

    我要勇敢地把我的帽子扔到这只戒指上(虽然很古老),看看我能得到多少选票。

    这是我的尝试,似乎在我测试过的任何地方都有效(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。

    它是如何工作的?

    …我听到你问-好吧,诚实的回答是,我没有 真的? 知道。在我测试的每一个地方都要花很多时间才能使它正常工作,而且它的确切效果会因运行位置的不同而略有不同。

    下面是它背后的想法:

    • 将函数设置为以尽可能低的间隔运行。我所看到的任何现实解决方案背后的基本概念都是在调度程序中填充比框架巴斯特更多的事件。
    • 每次函数启动时,尝试更改顶部框架的位置。要求比较明显。
    • 同时安排一个功能立即运行,这将需要很长的时间才能完成(从而阻止框架阻挡器干扰位置更改)。我选择了一个同步的xmlhttpRequest,因为它是我能想到的唯一机制,它不需要(或者至少要求)用户交互,也不会占用用户的CPU时间。

    为了我 http://mysite.tld/page-that-takes-a-while-to-load (XHR的目标)我使用了一个如下所示的PHP脚本:

    <?php sleep(5);
    

    发生什么事了?

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

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

    显然不是。起初,我将XHR指向一个返回404的URL——这在Firefox中不起作用。然后我尝试了 sleep(5); 我最终找到了这个答案的方法,然后我开始以各种方式玩起睡眠时间。我找不到真正的行为模式,但我确实发现,如果它太短,特别是火狐不会玩球(Chrome和IE似乎表现得相当好)。我不知道“太短”的真正定义是什么,但5秒 似乎 每次工作。


    如果任何一个经过的Javascript Ninjas想要更好地解释正在发生的事情,为什么这是(可能)错误的,不可靠的,他们见过的最糟糕的代码等等,我会很乐意倾听。

        11
  •  5
  •   SilverlightFox    10 年前

    到2015年,您应该使用CSP2 frame-ancestors 指示。这是通过HTTP响应头实现的。

    例如

    Content-Security-Policy: frame-ancestors 'none'
    

    当然,还没有多少浏览器支持CSP2,所以最好将旧的 X-Frame-Options 页眉:

    X-Frame-Options: DENY
    

    我建议两者都包括,否则你的网站将继续受到 Clickjacking 在旧浏览器中进行攻击,当然,即使没有恶意意图,您也会得到不受欢迎的帧。现在大多数浏览器都会自动更新,但是由于传统的应用程序兼容性原因,您仍然倾向于让公司用户停留在旧版本的Internet Explorer上。

        12
  •  4
  •   D'Arcy Rittich    16 年前

    好吧,您可以修改计数器的值,但这显然是一个脆弱的解决方案。在确定站点不在框架内之后,您可以通过Ajax加载您的内容——这也不是一个很好的解决方案,但它希望避免在卸载之前触发事件(我假设)。

    编辑:另一个想法。如果检测到您在一个框架中,请要求用户禁用javascript,然后单击将您带到所需URL的链接(传递一个让您的页面知道的查询字符串,告诉用户他们可以在javascript出现后重新启用)。

    编辑2:去核化-如果你发现你在一个框架,只需删除你的文件正文内容和打印一些讨厌的信息。

    编辑3:您能枚举顶部文档并将所有函数设置为空(甚至是匿名函数)吗?

        13
  •  4
  •   Marius    16 年前

    如果在buster代码之后添加一个警报,那么该警报将暂停javascript线程,并让页面加载。这就是StackOverflow所做的,它会从我的iframes中释放出来,即使我使用了框架破坏工具。它也适用于我的简单测试页面。这只在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>
    
        14
  •  3
  •   Jeff Atwood    16 年前

    我想你就快到了。你试过了吗?

    window.parent.onbeforeunload = null;
    window.parent.location.replace(self.location.href);
    

    或者,或者:

    window.parent.prevent_bust = 0;
    

    注:我实际上没有测试过这个。

        15
  •  2
  •   Christoph    16 年前

    再三给巴斯特打电话怎么样?这将创造一个竞赛条件,但人们可能希望巴斯特能出类拔萃:

    (function() {
        if(top !== self) {
            top.location.href = self.location.href;
            setTimeout(arguments.callee, 0);
        }
    })();
    
        16
  •  2
  •   sth    15 年前

    如果您查看 setInterval() 它们通常是单个数字,因此您通常可以用一行代码禁用所有此类中断:

    for (var j = 0 ; j < 256 ; ++j) clearInterval(j)
    
        17
  •  2
  •   Phcityonweb    14 年前

    我可能只是找到了一个破解框架克星JavaScript的方法。在我的javascript函数中使用getelementsbyname,我已经在frame buster和实际frame buster脚本之间设置了一个循环。 看看这篇文章。 http://www.phcityonweb.com/frame-buster-buster-buster-2426

        18
  •  0
  •   cwallenpoole    15 年前

    setInterval和setTimeout创建一个自动递增的间隔。每次调用setTimeout或setInterval时,这个数字都会增加一个,这样,如果调用setTimeout,就会得到当前的最高值。

       var currentInterval = 10000;
       currentInterval += setTimeout( gotoHREF, 100 );
       for( var i = 0; i < currentInterval; i++ ) top.clearInterval( i );
       // Include setTimeout to avoid recursive functions.
       for( i = 0; i < currentInterval; i++ )     top.clearTimeout( i );
    
       function gotoHREF(){
               top.location.href = "http://your.url.here";
       }
    

    由于有10000个同时运行的设置间隔和设置超时几乎是闻所未闻的,并且由于设置超时返回“创建的最后一个间隔或超时+1”,并且由于top.clearInterval仍然是可访问的,这将击败黑帽攻击框架网站,如前所述。

        19
  •  0
  •   B.F.    11 年前

    使用htaccess可避免高顶进框架集、iframe和任何内容(如图像)。

    RewriteEngine on
    RewriteCond %{HTTP_REFERER} !^http://www\.yoursite\.com/ [NC]
    RewriteCond %{HTTP_REFERER} !^$
    RewriteRule ^(.*)$ /copyrights.html [L]
    

    这将显示版权页,而不是预期的。