435

假设您不希望其他网站将您的网站“框定”在<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>

此代码执行以下操作:

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

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

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

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

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

4

20 回答 20

150

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

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

单击此链接以解决此问题

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

于 2009-06-18T13:21:16.620 回答
39

我们在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>
于 2012-12-04T17:26:26.810 回答
30

想出了这个,它似乎至少在 Firefox 和 Opera 浏览器中有效。

if(top != self) {
 top.onbeforeunload = function() {};
 top.location.replace(self.location.href);
}
于 2009-06-06T04:55:18.953 回答
27

考虑到当前为 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-if​​rame-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 和 Collin Jackson 提出(2010 年)

于 2013-05-16T20:53:18.023 回答
19

想了一会儿,我相信这会告诉他们谁是老大……

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

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

于 2009-06-18T14:40:02.810 回答
9

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

例如

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

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

X-Frame-Options: DENY

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

于 2015-07-08T09:01:01.293 回答
6

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

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

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

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

这是代码。您需要将标准 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>
于 2009-06-19T01:47:38.370 回答
6
if (top != self) {
  top.location.replace(location);
  location.replace("about:blank"); // want me framed? no way!
}
于 2009-06-19T15:23:24.917 回答
6

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

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

它是如何工作的?

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

以下是其背后的想法:

  • 设置一个函数以尽可能低的间隔运行。我所见过的任何现实解决方案背后的基本概念是用比帧 buster-buster 更多的事件来填充调度程序。
  • 每次函数触发时,尝试更改顶部框架的位置。相当明显的要求。
  • 还要安排一个功能立即运行,这将需要很长时间才能完成(从而阻止 frame 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 忍者想要更好地解释发生了什么,为什么这(可能)是错误的、不可靠的、他们见过的最糟糕的代码等等,我会很乐意听。

于 2012-08-15T15:38:21.250 回答
5

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

于 2009-06-12T01:44:09.500 回答
4

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

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

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

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

于 2009-06-06T04:40:24.230 回答
4

如果您在 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>
于 2009-07-22T09:17:09.400 回答
3

我想你几乎在那里。你有没有尝试过:

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

或者,或者:

window.parent.prevent_bust = 0;

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

于 2009-06-06T04:48:32.083 回答
3

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

for (var j = 0 ; j < 256 ; ++j) clearInterval(j)
于 2010-02-21T08:57:59.750 回答
2

反复呼叫破坏者怎么样?这将创造一种竞争条件,但人们可能希望破坏者脱颖而出:

(function() {
    if(top !== self) {
        top.location.href = self.location.href;
        setTimeout(arguments.callee, 0);
    }
})();
于 2009-06-06T09:20:33.457 回答
2

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

于 2011-02-20T14:42:48.533 回答
0

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 仍然可以访问,这将击败黑帽攻击帧上述网站。

于 2009-12-23T15:40:15.697 回答
0

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

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

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

于 2013-12-18T19:14:53.450 回答
0

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

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事件的函数中。

于 2020-08-25T11:22:40.437 回答