假设你不希望其他网站在<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程序员,所以这是我对你的挑战:嘿,老兄,你能打破框架破坏老兄吗?
考虑到当前的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>
最初由Gustav Rydstedt, Elie Bursztein, Dan Boneh和Collin Jackson提出(2010)
所有建议的解决方案都直接强制改变顶部窗口的位置。如果用户想要帧在那里怎么办?例如,搜索引擎的图像结果中的顶部帧。
我写了一个原型,默认情况下所有输入(链接、表单和输入元素)都是禁用的,激活时什么都不做。
如果检测到包含帧,输入将被禁用,并在页面顶部显示警告消息。警告消息包含一个链接,该链接将在新窗口中打开该页的安全版本。这可以防止页面被用于点击劫持,同时仍然允许用户在其他情况下查看内容。
如果没有检测到包含帧,则输入被启用。
这是代码。您需要将标准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>