【问题标题】:Frame Buster Buster ... buster code neededFrame Buster Buster ...需要破坏者代码
【发布时间】:2010-10-31 20:01:33
【问题描述】:

假设您不希望其他网站在<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 状态代码 204 的页面,这不会导致浏览器在任何地方导航

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

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

  • 尝试通过onbeforeunload = null 清除onbeforeunload 事件无效
  • 添加alert() 停止进程让用户知道它正在发生,但不会以任何方式干扰代码;点击确定让破坏继续正常进行
  • 我想不出任何方法来清除setInterval() 计时器

我不是一个 JavaScript 程序员,所以这是我向你提出的挑战:嘿,破坏者,你能破坏框架破坏破坏者吗?

【问题讨论】:

  • 我不确定 frame-buster-buster 是否真的有效......当我尝试测试它时(重定向到我设置为返回 204 的处理程序),它会阻止我导航 页面外的任何地方 - 包括在地址栏中输入内容!我必须关闭浏览器选项卡并打开一个新选项卡才能到达任何地方。所以换句话说,我不确定这需要一个解决方案,因为想要被破坏的框架破坏者是......从一开始就破坏了。 :) (或者我搞砸了我的测试,这永远不会发生......);)
  • Matt,上面发布的 frame-buster-buster 代码肯定有效。一个.. 呃.. 我的朋友.. 告诉我.. 关于它。或者其他的东西。 :)
  • Jeff,您是否在同一个域中使用两个窗口进行测试?看起来你是因为如果你不是,那么安全限制会阻止你修改'onBeforeUnload'
  • 附带说明:发布示例时,请使用 RFC 2606 ietf.org/rfc/rfc2606.txt 中指定的 example.org 等域
  • 关于反反对策的总主题:galactanet.com/comic/view.php?strip=209

标签: javascript html iframe framebusting


【解决方案1】:

您可以通过使用postMessage() 方法来改进整个想法,以允许某些域访问和显示您的内容,同时阻止所有其他域。首先,父容器必须通过向试图显示您的页面的iframecontentWindow 发布消息来介绍自己。并且您的页面必须准备好接受消息,

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event) {
  // Use event.origin here like
  if(event.origin == "https://perhapsyoucantrustthisdomain.com"){
  // code here to block/unblock access ... a method like the one in user1646111's post can be good.
  }
  else{
  // code here to block/unblock access ... a method like the one in user1646111's post can be good.
  }
}

最后不要忘记将东西包装在等待load事件的函数中。

【讨论】:

    【解决方案2】:

    想出了这个,它似乎至少可以在 Firefox 和 Opera 浏览器中工作。

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

    【讨论】:

    • Jani 和 Jeff 的解决方案(经过编辑)都是正确的,并且效果相同;让 Jani 接受,因为他的解决方案无需任何编辑即可正常工作
    • 这仅在两个窗口属于同一域时才有效;当您想从框架中逃脱时,这种情况很少发生。
    • 如果涉及嵌套框架,您必须遍历框架链并删除所有 onbeforeunload 处理程序,而不仅仅是顶部的处理程序!
    • 重要说明:这对我有用,因为 iframe src= 是动态设置的,因此跨域策略无效。 J-P 绝对正确,在静态 src= 中这是行不通的。
    • 好的,现在有人能想出一个框架克星克星克星克星吗?
    【解决方案3】:

    截至 2015 年,您应该为此使用 CSP2 的 frame-ancestors 指令。这是通过 HTTP 响应标头实现的。

    例如

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

    当然,目前支持 CSP2 的浏览器并不多,因此最好包含旧的 X-Frame-Options 标头:

    X-Frame-Options: DENY
    

    我建议无论如何都包括这两个,否则您的网站将继续容易受到旧浏览器中的Clickjacking 攻击,当然,即使没有恶意,您也会得到不受欢迎的框架。如今,大多数浏览器都会自动更新,但是出于旧版应用程序兼容性的原因,您仍然倾向于让企业用户卡在旧版本的 Internet Explorer 上。

    【讨论】:

    • 所有主流浏览器现在都支持 CSP。这是2019年和可预见的未来的正确答案。
    【解决方案4】:

    使用 htaccess 避免劫持框架集、iframe 和任何类似图像的内容。

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

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

    【讨论】:

    • 这取决于引荐来源网址,它是 a) 并不总是设置(由于浏览器设置或扩展或仅仅因为引荐页面使用 HTTPS 而没有使用&lt;meta name="referrer" …/&gt; 和 b)在单击链接时也设置,因此您也不允许链接到您的页面并破坏网络。
    【解决方案5】:

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

    这是我的尝试,它似乎在我测试过的所有地方都有效(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);
    }());
    

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

    它是如何工作的?

    ...我听到你问了——好吧,诚实的回答是,我真的知道。我花了很多时间才让它在我测试的任何地方都能正常工作,而且它的确切效果会根据你运行它的位置而略有不同。

    这是它背后的想法:

    • 将函数设置为以尽可能低的间隔运行。我所见过的任何现实解决方案背后的基本概念是用比帧 buster-buster 更多的事件来填充调度程序。
    • 每次函数触发时,尝试更改顶部框架的位置。相当明显的要求。
    • 还安排一个功能立即运行,这将需要很长时间才能完成(从而阻止帧破坏者干扰位置更改)。我选择了同步 XMLHttpRequest,因为它是我能想到的唯一一种不需要(或至少要求)用户交互并且不会占用用户 CPU 时间的机制。

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

    <?php sleep(5);
    

    会发生什么?

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

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

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


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

    【讨论】:

    • 看来你可以删除所有令人担忧的句子
    【解决方案6】:

    考虑到为 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)提出的

    【讨论】:

      【解决方案7】:

      我们在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>
      

      【讨论】:

      • 宾果游戏!不知道为什么它没有被更多人赞成,因为它是最好的答案(在 X-Frame-Options 答案旁边,但最好将两者结合起来)
      • 应该选择这个答案或我的答案:)
      • 我喜欢你同时使用这个和 X-Frame-Options:deny 答案的想法。
      • 这需要JS,恕我直言,这是一个相当大的负担。
      • +1 - 这是不能使用 X-FRAME-OPTIONS 时的最佳答案。 (例如,当您需要根据推荐人有条件地允许或拒绝时。)
      【解决方案8】:

      想了一会儿,我相信这会让他们知道谁是老板……

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

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

      【讨论】:

        【解决方案9】:

        【讨论】:

        【解决方案10】:

        我可能刚刚找到了一种破坏框架破坏者破坏者javascript的方法。在我的 javascript 函数中使用 getElementsByName,我在 frame buster 和实际的 frame buster buster 脚本之间设置了一个循环。 看看这个帖子。 http://www.phcityonweb.com/frame-buster-buster-buster-2426

        【讨论】:

          【解决方案11】:

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

          for (var j = 0 ; j < 256 ; ++j) clearInterval(j)
          

          【讨论】:

            【解决方案12】:

            setInterval 和 setTimeout 创建一个自动递增的间隔。每次调用 setTimeout 或 setInterval 时,此数字都会增加 1,因此如果您调用 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 个 setIntervals 和 setTimeouts 工作,并且由于 setTimeout 返回“创建的最后一个间隔或超时 + 1”,并且由于 top.clearInterval 仍然可以访问,这将击败黑帽对上述框架网站的攻击。

            【讨论】:

              【解决方案13】:

              如果您在 buster 代码之后添加警报,则警报将停止 javascript 线程,并让页面加载。这就是 StackOverflow 所做的,即使我使用 framebusting buster,它也会从我的 iframe 中消失。它也适用于我的简单测试页面。这仅在 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】:
                if (top != self) {
                  top.location.replace(location);
                  location.replace("about:blank"); // want me framed? no way!
                }
                

                【讨论】:

                  【解决方案15】:

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

                  我编写了一个原型,默认情况下所有输入(链接、表单和输入元素)都被禁用和/或在激活时不执行任何操作。

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

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

                  这里是代码。您需要将标准 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>
                  

                  【讨论】:

                  • 这个问题是框架制造商可以使用 position:absolute 将活动按钮放在非活动按钮的顶部,用户只会看到你的网页并认为他们正在点击你的按钮。跨度>
                  • 仍然会显示警告消息,但当然很容易按照您的建议覆盖安全页面的链接。但是,如果您可以简单地复制页面并达到相同的效果,为什么还要费尽心思构建我的页面以让人们点击熟悉的按钮呢?上面的代码主要是防止点击劫持。如果您在另一个页面上方不可见地显示我的页面,则无法在我的网站上调用操作。
                  • 如果将其放置在 IE8 受限区域框架或 Chrome 沙盒框架中,则 Javascript 将永远不会运行。我想知道在这些情况下需要进行哪些修改
                  【解决方案16】:

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

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

                  点击此链接解决此问题

                  不是最好的,但我看不出他们有什么办法可以摆脱这种情况。

                  【讨论】:

                  • 我已经尝试过这个并且这个工作。我喜欢这个解决方案的另一点是,它可以让用户在访问您的内容之前了解他/她在什么样的网站上。示例代码: if (parent.frames.length > 0) { top.location.replace(document.location); setTimeout(function() { if (parent.frames.length > 0) { document.location = "google.com"; } }, 10); }
                  • 这不仅是避免滥用的好方法,而且对于那些可能想要 iframe 您的网站只是为了看一眼但不允许使用它的网站非常友好。理想情况下,我认为应该使用网站主页的屏幕截图,并解释为什么它不能在覆盖在顶部的 iframe 中使用。
                  • 这就是 Facebook 的做法。
                  • 但是如果破坏网站反过来会创建一个虚假的反反反反...(不知道我们现在有多少反) lighbox div 本身呈现网络钓鱼链接或随便...待定
                  • 另一个想法是用document.write("");之类的东西完全清除页面(在你确定它被框起来之后
                  【解决方案17】:

                  好的,所以我们知道它在一个框架中。因此,我们将 location.href 指向另一个特殊页面,并将路径作为 GET 变量。我们现在向用户解释发生了什么,并提供一个带有 target="_TOP" 选项的链接。它很简单,可能会起作用(尚未测试过),但它需要一些用户交互。也许您可以向用户指出有问题的网站,并在某个地方让点击劫持者对您的网站感到羞耻。只是一个想法,但它夜间工作..

                  【讨论】:

                    【解决方案18】:

                    如果还要反复呼叫破坏者呢?这将创造一种竞争条件,但人们可能希望破坏者脱颖而出:

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

                    【讨论】:

                      【解决方案19】:

                      我想你快到了。你试过了吗:

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

                      或者,或者:

                      window.parent.prevent_bust = 0;
                      

                      注意:我实际上并没有对此进行测试。

                      【讨论】:

                      • 我编辑了您的代码示例(对 parent 的测试似乎失败了),但编辑后的版本似乎可以工作!
                      • 酷。用未经测试的代码回答总是很棘手 - 我这样做至少是为了让这个想法得到理解 - 让可怜的提问者调试。 :)
                      • 如果父级在不同的域上将不起作用,很可能就是这种情况!
                      【解决方案20】:

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

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

                      编辑 2:去核 - 如果你检测到你在一个框架中,只需删除你的文档正文内容并打印一些讨厌的消息。

                      编辑 3:您能否枚举顶部文档并将所有函数设置为 null(即使是匿名函数)?

                      【讨论】:

                      • Outlook(以前的 Hotmail)如果无法脱离框架,就会“变成核” - 它将&lt;body&gt; 的全部内容放在设置为&lt;plaintext&gt;&lt;plaintext&gt; 标记中@ .很有效。
                      猜你喜欢
                      • 1970-01-01
                      • 2012-02-06
                      • 2011-11-27
                      • 1970-01-01
                      • 2014-10-23
                      • 1970-01-01
                      • 2020-07-30
                      • 1970-01-01
                      相关资源
                      最近更新 更多