框定假人老兄……需要Buster代码

假设你不希望其他网站在<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 = null清除onbeforeunload事件没有效果
  • 添加alert()停止进程,让用户知道它正在发生,但不以任何方式干扰代码;单击OK,让破坏照常进行
  • 我想不出任何方法来清除setInterval()定时器

我不是一个JavaScript程序员,所以这是我对你的挑战:老兄,你能抓到那个捣蛋鬼吗?

149075 次浏览

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

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

编辑2:去核——如果你发现你在一个框架,只要删除你的文档主体内容和打印一些讨厌的消息。

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

我想你已经差不多了。你有没有试过:

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

或者,或者:

window.parent.prevent_bust = 0;

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

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

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

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

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

好的,我们知道它们在一个坐标系中。所以我们定位。href到另一个特殊页面,其路径为GET变量。现在我们向用户解释发生了什么,并提供一个带有target="_TOP"选项的链接。它很简单,可能会工作(还没有测试过),但它需要一些用户交互。也许你可以指出违规网站的用户,并使点击jackers的耻辱大厅到你的网站的某处。只是一个想法,但它是夜间工作。

我不确定这是否可行,但如果你不能打破框架,为什么不只是显示一个警告。例如,如果你的页面不是“首页”;创建一个setInterval方法来尝试打断帧。如果经过3或4次尝试,你的页面仍然不是顶部页面-创建一个div元素,覆盖整个页面(模态框)的消息和链接,如…

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

单击此链接可修复此问题

不是最好的,但我不认为他们可以通过剧本来解决这个问题。

在思考了一段时间后,我相信这会让他们知道谁才是老大……

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

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

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

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

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

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

这是代码。您需要将标准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>
if (top != self) {
top.location.replace(location);
location.replace("about:blank"); // want me framed? no way!
}

如果您在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>

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个同时工作的setinterval和setTimeouts,并且由于setTimeout返回“最后创建的间隔或超时+ 1”,并且由于top。clearInterval仍然是可访问的,这将击败黑帽攻击框架网站上面所描述的。

如果你看一下setInterval()返回的值,它们通常是单个数字,所以你通常可以用一行代码禁用所有这样的中断:

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

我可能只是刚刚得到了一种方法来打破框架buster javascript。在我的javascript函数中使用getElementsByName,我已经在框架buster和实际的框架buster脚本之间设置了一个循环。 看看这篇文章。http://www.phcityonweb.com/frame-buster-buster-buster-2426 < / p >

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

以下是我的尝试,在我测试的所有地方(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干扰位置更改)。我选择了同步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高手想要更好地解释一下发生了什么,为什么这(可能)是错误的,不可靠的,为什么这是他们见过的最糟糕的代码等等,我很乐意听。

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

考虑到当前的HTML5标准为iframe引入了沙盒,当攻击者使用沙盒时,本页中提供的所有帧破坏代码都可以被禁用,因为它限制了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>

最初由古斯塔夫·赖德施泰特,埃利·伯斯泰因,丹·博内,科林·杰克逊(2010)提出的

使用htaccess来避免帧集,iframe和任何像图像这样的内容。

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

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

从2015年起,你应该使用CSP2的frame-ancestors指令来实现这一点。这是通过HTTP响应头实现的。

如。

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

当然,目前还没有很多浏览器支持CSP2,所以包含旧的X-Frame-Options头文件是明智的:

X-Frame-Options: DENY

我建议无论如何都要包含这两者,否则你的网站将继续在旧浏览器中容易受到“点击劫持”攻击,当然,即使没有恶意,你也会得到不受欢迎的帧。现在大多数浏览器都能自动更新,但是由于遗留应用程序兼容性的原因,企业用户仍然会被困在旧版本的Internet Explorer上。

你可以通过使用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事件的函数中。