web安全 - 点击劫持(Clickjacking)
1. 什么是点击劫持
点击劫持是指攻击者使用多个透明或不透明层来诱使用户在打算点击顶层页面时点击另一个页面上的按钮或链接。因此攻击者正在劫持针对其页面的点击,并将它们路由到另一个页面,该页面很可能是另一个应用程序或域。
例如,假设攻击者构建了一个网站,该网站上有一个按钮,上面写着“单击此处获取免费iPod”。然而,在该网
站的顶部,攻击者已经使用您的邮件账户加载了一个iframe,并将“删除所有消息”按钮直接排在“免费iPod”
按钮的顶部。受害者试图点击“免费iPod”按钮,但实际上点击了不可见的“删除所有消息”按钮。本质上,攻击
者“劫持”了用户的点击,因此被叫做“点击劫持”。
2. 点击劫持防御策略
-
使用X-Frame-Options 或者 CSP(frame-ancestors) HTTP 头部设置,阻止浏览器在框架中加载页面。
-
使用SameSite cookie 属性将页面加载到框架中时防止包含会话cookie。
-
在页面中使用防御性代码以试图阻止它在框架中加载,这些防御性javascript代码被称为破坏器(frame-buster)。
注意:以上这些防御措施都是相互独立的,并且在可能的情况下应实施多种方案,从而实现纵深防御。
3. 使用内容安全策略(CSP)frame-ancestors 指令进行防御
frame-ancestors 指令可用于Content-Security-Policy HTTP响应标头中,以指示是否应允许浏览器在
frame或iframe中呈现页面。站点可以通过确保其内容不嵌入其他站点来使用它来避免点击劫持攻击。
CSP frame-ancestors 的使用:
- Content-Security-Policy: frame-ancestors 'none';
此设置阻止任何域使用框架对页面进行引用,除非特殊需要,建议使用此设置。
- Content-Security-Policy: frame-ancestors 'self';
此设置只允许当前站点在框架引用页面。
- Content-Security-Policy: frame-ancestors 'self' *.somesite.com https://myfriend.site.com
此设置允许当前站点,以及somesite.com使用任何协议的任何页面,以及myfriend.site.com
仅在443端口上使用HTTPS。
CSP frame-ancestors的使用局限:
- 浏览器支持:并非所有主流浏览器都支持CSP frame-ancestors 指令。
- 在某些浏览器X-Frame-Options的优先级高于CSP frame-ancestors。
4. 使用X-Frame-Options HTTP 响应标头进行防御
X-Frame-Options HTTP响应标头可用于指示是否允许浏览器在frame 或iframe中呈现页面。网站
可以使用它来避免点击劫持攻击,方法是确保其内容不会嵌入到其他网站中。为所有包含HTML内容的
响应设置X-Frame-Options 标头。可以设置的值为"DENY", "SAMEORIGIN", "ALLOW-FROM uri"
X-Frame-Optoins 的使用:
- DENY, 此设置阻止任何域使用框架对页面进行引用,除非特殊需要,建议使用此设置。
- SAMEORIGIN, 此设置只允许当前站点在框架引用页面。
- ALLOW-FROM uri, 只允许特定的域在框架中引用页面。
如何实施:
- 手动将HTTP响应标头添加到每个页面。
- 实现一个过滤器,该过滤器自动将标题添加到每个页面,或者在web应用程序服务器级别的防火墙中添加。
X-Frame-Options 的使用局限:
- 每页策略规范: 需要为每个页面指定策略,使部署复杂化。在登录时为整个站点提供强制执行它的能力可以
简化采用。
- 多域站点问题: 当前的实现不允许网站管理员提供允许构建页面的域列表。
- ALLOW-FROM url 浏览器支持:ALLOW-FROM选贤是一个相对较新的添加,可能尚未得到所有浏览器支持。
- 不支持多个选项:无法允许当前站点和第三方站点构建相同的响应,浏览器仅支持一个X-Frame-Options标头。
- 嵌套框架不适用于SAMORIGIN和ALLOW-FROM
- X-Frame-Options被丢弃:虽然主流浏览器都支持X-Frame-Options标头,但它已被CSP Level 2规范中的
frame-ancestors指令取代。
- 代理:如果web代理剥离X-Frame-Options标头,则该站点将失去其框架保护。
5. 使用SameSite Cookies 进行防御
SameSite cookie 属性主要用于防御跨站点请求伪造(CSRF),但是它也可以提供针对点击劫持攻击的保护。
SameSite 属性为strict或lax的Cookie不会包含在iframe框架内的页面发送的请求中。这意味着,如果会话
cookie被标记为SameSite,则任何需要对受害者进行身份验证的Clickjacking攻击都将不起作用,因为不会
发送cookie。
SameSite Cookie属性使用局限:
如果Clickjacking攻击不需要用户进行身份验证,则该属性不会提供任何保护。此外,虽然大多数现代浏览器
都支持SameSite属性,但仍有一些用户的浏览器不支持它。此属性的使用应被视为纵深防御方法的一部分,不应
将其作为针对点击劫持的唯一保护措施。
6. 使用框架破坏脚本进行防御(frame-breaker script)
防御点击劫持的一种方法是在每个不应被框架引用的页面中包含一个“frame-breaker”脚本。即使在不支持
X-Frame-Options 响应标头的旧版本浏览器中,也能防止被其他站点框架引用。
实现方案:
// 在style标签增加一个id
<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>
如果当页面可能是被框架可引用时,则可以使用window.confirm() 通过通知用户他们将要执行的操作来帮助
减轻点击劫持,如果父窗口和当前被引用页面不是同一个域,则window.confirm()会提示源自哪个域。
某些框架破坏脚本可能被绕过:
- 双重框架
//breaking frame code
if (top.location != self.location)
parent.location = self.location
}
// Attacker top frame
<iframe src="attacker2.html">
// Attacker sub-frame:
<iframe src="http://www.vicitim.com">
- onBeforeUnload Event
// 攻击者会注册一个框架由于导航而卸载的事件处理程序,从而返回一个
// 字符串给用户,从而使用户取消导航,从而导致框架破坏脚本失效。
<script>
window.onbeforeunload = function(){
return "Asking the user nicely";
}
</script>
<iframe src="http://www.paypal.com">
- No-Content Flushing
/**
* 虽然之前的攻击需要用户交互,但同样的攻击可以在不提示用户的情况下进行,大多数浏览器允许攻击者
* 通过重复提交导航请求到响应为“204 - 无内容“的站点来自动取消onBeforeUnload事件处理程序中的传入
* 导航请求。
*/
var preventbust = 0
window.onbeforeunload = function() { killbust++ }
setInterval( function() {
if(killbust > 0){
killbust = 2;
window.top.location = 'http://nocontent204.com'
}
}, 1);
<iframe src="http://www.victim.com">
- 利用xss过滤器
// frame-breaking code
<script>
if(top != self) {
top.location = self.location;
}
</script>
// attacker code
// XSS过滤器会将参数<script> if 匹配到受害者的框架破坏脚本的开头,从而禁用受害者页面中的所有
// 内联脚本,包括框架破坏脚本。
<iframe src="http://www.victim.com/?v=<script>if''>
- 重定义top.location
一些现代浏览器将locaiton视为所有上下文中的特殊不可变属性,但是在可以重新定义location变量的IE7
和Safari4.0.4中,框架中任何试图读取top.location的框架破坏代码都将通过尝试读取另一个域中的局部
变量来违反安全性,同样任何通过分配top.location进行导航的尝试都将失败。
// frame-breaking code
if(top.location != self.location) {
top.location = self.location;
}
// attacker code for IE 7
<script>var location = "clobbered";</script>
<iframe src="http://www.victim.com"></iframe>
// attacker code for Safari 4.0.4
<script>
window.defineSetter("location", function(){});
</script>
- 禁用脚本执行
大多数框架破坏脚本依赖于页面中的javascript来检测框架并自行破坏。如果在框架的上下文中禁用javascript
则框架破坏代码将不会运行。
// In IE8
<iframe src="http://www.victim.com" security="restricted"></iframe>
// In Chrome
<iframe src="http://www.victim.com" sandbox></iframe>
// Firefox and IE
document.designMode = "on";
参考:https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html