点击劫持攻击
点击劫持攻击就是恶意的页面 以用户的名义 点击受害网站。
原理
- 访问者被恶意页面吸引
- 页面上有一个看起来无害的链接(例如:点我,超好玩)
- 恶意页面在这个按钮下方有一个透明的
<iframe>,这个src来自于bilibili,这就使得“点赞”按钮恰好位于该链接下方。这是使用透明+z-index实现的 - 用户点击这个链接,实际上点击的是点赞按钮
真实情况下点击后可能会损害用户的其他利益,这里用点赞代替。
示例
index.html 恶意网站
html1<style> 2 iframe { 3 /* 来自受害网站的 iframe */ 4 width: 400px; 5 height: 100px; 6 position: absolute; 7 top: 20px; 8 opacity: 0.5; /* 在实际中为 opacity:0 */ 9 z-index: 1; 10 } 11</style> 12 13<div>点击即可变得富有:</div> 14 15<!-- 来自受害网站的 url --> 16<iframe src="bilibili.html" frameborder="0"></iframe> 17 18<button>点这里!</button> 19 20<div>……你很酷(我实际上是一名帅气的黑客)!</div>
bilibili.html 受害网站
html1<input type="button" onclick="alert('你成功点了个赞!')" value="点我!" />
整体的样子是这样的
上面的例子中,我们有一个半透明的<iframe src="bilibili.html" frameborder="0"></iframe>,它位于点击按钮之上。用户的点击实际上会点击到 iframe 上,但是用户不知情,因为 iframe 是透明的。
如果用户此时登陆了 bilibili,那么点击劫持攻击行为就成功了。(B 站需要登陆才能点赞)
点击劫持行为一般是对点击事件,而非键盘事件。
很简单的原因—— 因为 iframe 是不可见的,用户键入的内容会被隐藏,当用户发现输入的字符看不见时,通常会停止打字。
传统防御措施(弱)
最早的防御措施是一段禁止在 frame 中打开页面的 JavaScript 代码,它是这样写的:
javascript1if (top != window) { 2 top.location = window.location 3}
意思是如果 window 发现它并不在顶部,那就顶部的 location 设置为自己的。
但是这种方法并不牢靠。
有多种方法可以让上面的代码无效,例如:
-
top 页面监听
beforeunload事件,阻止页面离开。javascript1window.onbeforeunload = function () { 2 return false 3}当
iframe试图更改top.location时,访问者会收到一条消息,询问他们是否要离开页面。在大多数情况下,访问者会做出否定的回答,因为他们并不知道还有这么一个 iframe,他们所看到的只有顶级页面,他们没有理由离开。所以
top.location不会变化! -
Sandbox 特性
sandbox 特性的限制之一就是导航。沙箱化的 iframe 不能更改
top.location。html1<iframe sandbox="allow-scripts allow-forms" src="facebook.html"></iframe>上述代码中添加了允许脚本和允许表单的属性,但没添加
allow-top-navigation,因此更改top.location是被禁止的。
有效的防御措施
服务端设置X-Frame-Options
服务端的 header 属性X-Frame-Options允许或禁止在 frame 中显示页面。
这个 header 包含 3 个值:
-
DENY
始终禁止在 frame 中显示此页面
-
SAMEORIGIN
允许在和父文档同源的 frame 中显示此页面
-
ALLOW-FROM domain
允许在来自给定域的父文档的 frame 中显示此页面
例如,百度就是这样设置的
http1X-Frame-Options:sameorigin
用一个 div 覆盖整个页面
用一个覆盖全屏的<div>拦截所有点击,只有确定不需要保护时,才移除<div>按钮
html1<style> 2 #protector { 3 height: 100%; 4 width: 100%; 5 position: absolute; 6 left: 0; 7 top: 0; 8 z-index: 99999999; 9 } 10</style> 11 12<div id="protector"> 13 <a href="/" target="_blank">前往网站</a> 14</div> 15 16<script> 17 // 如果顶级窗口来自其他源,这里则会出现一个 error 18 // 但是在本例中没有问题 19 if (top.document.domain == document.domain) { 20 protector.remove() 21 } 22</script>
Samesite cookie 特性
具有 samesite 特性的 cookie 仅在网站是通过直接方式打开(而不是通过 frame 或其他方式)的情况下才发送到网站。通过这种方式,即使用户点击了,cookie 也不会被发送出去,这样很多行为都会失效。
它可以这样设置:
http1Set-Cookie: authorization=secret; samesite
当不使用 cookie 时,samesite cookie 特性将不会有任何影响。这可以使其他网站能够轻松地在 iframe 中显示我们公开的、未进行身份验证的页面。
唯一不好之处就是有些网站不使用 cookie 对用户身份进行验证,那还是会受到劫持攻击。
总结
点击劫持是一种诱骗用户在不知情的情况下点击恶意网站的方式。如果是重要的点击操作,这是非常危险的。
我们有三种有效的方法来防范这种攻击:
- 设置
X-Frame-Options: SAMEORIGIN不让非同源的网站用 frame 嵌入 - 用一个 div 覆盖,只有满足条件才删除这个 div,这样用户就没办法点到 frame 中的按钮了
- 通过
samesit特性,仅在网站通过直接方式打开的(而不是通过 frame 或其他方式)的情况下才发送 cookie,这样即使用户点击到也是无效的操作,这种方法仅适用于用 cookie 做身份验证的网站。