1
1
# 点击劫持攻击
2
2
3
- “点击劫持” 攻击即允许恶意网页 ** 以用户的名义** 点击 “受害站点 ”。
3
+ “点击劫持”攻击允许恶意页面 ** 以用户的名义** 点击“受害网站 ”。
4
4
5
- 许多站点都被这样攻击过 ,包括 Twitter、Facebook 和 Paypal 等等许多网站 。当然,目前它们都已修复这个问题 。
5
+ 许多网站都被黑客以这种方式攻击过 ,包括 Twitter、Facebook 和 Paypal 等许多网站 。当然,它们都已经被修复了 。
6
6
7
7
## 原理
8
8
9
9
原理十分简单。
10
10
11
- 以下以 Facebook 为例解释点击劫持是如何运作的 :
11
+ 我们以 Facebook 为例,解释点击劫持是如何完成的 :
12
12
13
- 1 . 访问者被恶意网页吸引。此处略过如何被吸引的 。
14
- 2 . 页面上存在一个看起来无害的链接(比如:“马上有钱” 或者 “点我,超好玩!”)。
15
- 3 . 恶意网页在该链接之上放置一个透明 ` <iframe> ` 标签,其中 ` src ` 指向 facebook.com,如此一来, “点赞” 按钮恰好在链接上面。通常用 ` z-index ` 实现 。
16
- 4 . 如果用户试图点击该链接,实际上是点到了 “点赞” 按钮上 。
13
+ 1 . 访问者被恶意页面吸引。怎样吸引的不重要 。
14
+ 2 . 页面上有一个看起来无害的链接(例如:“变得富有”或者 “点我,超好玩!”)。
15
+ 3 . 恶意页面在该链接上方放置了一个透明的 ` <iframe> ` ,其 ` src ` 来自于 facebook.com,这使得 “点赞”按钮恰好位于该链接上面。这通常是通过 ` z-index ` 实现的 。
16
+ 4 . 用户尝试点击该链接时,实际上点击的是 “点赞”按钮 。
17
17
18
18
## 示例
19
19
20
- 以下是恶意网页的一般代码。为了更好的说明问题, ` <iframe> ` 标签设置成半透明状态(真正的恶意网页为全透明状态 ):
20
+ 这是恶意页面看起来的样子。为了清楚起见,我们将 ` <iframe> ` 设置成了半透明的(在真正的恶意页面中,它是全透明的 ):
21
21
22
22
``` html run height=120 no-beautify
23
23
<style >
@@ -27,138 +27,138 @@ iframe { /* 来自受害网站的 iframe */
27
27
position : absolute ;
28
28
top :0 ; left :-20px ;
29
29
*!*
30
- opacity : 0.5 ; /* 真实为 opacity:0 */
30
+ opacity : 0.5 ; /* 在实际中为 opacity:0 */
31
31
*/!*
32
32
z-index : 1 ;
33
33
}
34
34
</style >
35
35
36
- <div >马上有钱 :</div >
36
+ <div >点击即可变得富有 :</div >
37
37
38
38
<!-- 来自受害网站的 url -->
39
39
*!*
40
40
<iframe src =" /clickjacking/facebook.html" ></iframe >
41
41
42
- <button >点我!点我 !</button >
42
+ <button >点这里 !</button >
43
43
*/!*
44
44
45
- <div >...你会变帅(我才是帅黑客😜 )!</div >
45
+ <div >……你很酷(我实际上是一名帅气的黑客 )!</div >
46
46
```
47
47
48
48
完整的攻击示例如下:
49
49
50
50
[ codetabs src="clickjacking-visible" height=160]
51
51
52
- 例子中的半透明 ` <iframe src="facebook.html"> ` 覆盖在按钮之上。点击按钮实际上点击在 iframe 标签上,但由于 iframe 标签透明,这一动作对用户不可见 。
52
+ 在上面这个示例中,我们有一个半透明的 ` <iframe src="facebook.html"> ` ,我们可以看到,它位于按钮之上。点击按钮实际上会点击在 iframe 上,但这对用户不可见,因为 iframe 是透明的 。
53
53
54
+ 结果,如果访问者登陆了 Facebook(“记住我”通常是打开的),那么这个行为就会点一个“赞”。Twitter 上是 "Follow" 按钮。
54
55
55
- 因此,若访问者曾登陆 Facebook(“记住我” 开关打开),这个动作会使用户在 Facebook 上进行 “Like” 操作。Twitter 上是 “Follow” 操作。
56
-
57
- 下面的例子相同,但 ` iframe ` 设置为 ` opacity:0 ` 更符合实际情况:
56
+ 下面是相同的示例,但 ` iframe ` 的透明度设置为了 ` opacity:0 ` ,更符合实际情况:
58
57
59
58
[ codetabs src="clickjacking" height=160]
60
59
61
- 只需要在恶意网页中的链接正上方放置 ` <iframe> ` ,点击按钮就能发起攻击。通常用 CSS 就能实现 。
60
+ 我们进行攻击所需要做的 —— 就是将 ` <iframe> ` 放置在恶意页面中,使得按钮恰好位于链接的正上方。这样当用户点击链接时,他们实际上点击的是按钮。这通常可以通过 CSS 实现 。
62
61
63
- ``` smart header="点击劫持作用于点击事件 ,而非键盘事件"
64
- 此攻击仅影响鼠标操作 。
62
+ ``` smart header="点击劫持是对点击事件 ,而非键盘事件"
63
+ 此攻击仅影响鼠标行为(或者类似的行为,例如在手机上的点击) 。
65
64
66
- 从技术上讲,可以用 iframe 中的文本域覆盖原有的文本域实现攻击。所以当访问者试图聚焦网页中的 input 标签时, 实际上聚焦的是 iframe 中的 input 标签 。
65
+ 键盘输入很难重定向。 从技术上讲,我们可以用 iframe 的文本区域覆盖原有的文本区域实现攻击。因此,当访问者试图聚焦页面中的输入时, 实际上聚焦的是 iframe 中的输入 。
67
66
68
- 但是这里有个问题。访问者的所有输入都会被隐藏 ,因为该 iframe 是不可见的。
67
+ 但是这里有个问题。访问者键入的所有内容都会被隐藏 ,因为该 iframe 是不可见的。
69
68
70
69
当用户无法在屏幕上看到自己输入的字符时,通常会停止打字。
71
70
```
72
71
73
72
## 传统防御(弱 👎)
74
73
75
- 最古老的防御是一段禁止在非顶层页面中打开网页的 JavaScript 代码(所谓的 “framebusting”)。
74
+ 最古老的防御措施是一段用于禁止在 frame 中打开页面的 JavaScript 代码(所谓的 “framebusting”)。
76
75
77
- 如下所示 :
76
+ 它看起来像这样 :
78
77
79
78
``` js
80
79
if (top != window ) {
81
80
top .location = window .location ;
82
81
}
83
82
```
84
83
85
- 意思是: window 强制置顶,如果没在顶层,自动置顶 。
84
+ 意思是说:如果 window 发现它不在顶部,那么它将自动使其自身位于顶部 。
86
85
87
- 这个方法并不可靠,因为有许多方式可以绕过这个限制。下面就介绍几个 。
86
+ 这个方法并不可靠,因为有许多方式可以绕过这个限制。下面我们就介绍几个 。
88
87
89
- ### 阻塞顶层容器
88
+ ### 阻止顶级导航
90
89
91
- 在 [ beforeunload] ( info:onload-ondomcontentloaded#window.onbeforeunload ) 事件中阻塞 ` top.location ` 变更过渡 。
90
+ 我们可以阻止因更改 [ beforeunload] ( info:onload-ondomcontentloaded#window.onbeforeunload ) 事件处理程序中的 ` top.location ` 而引起的过渡(transition) 。
92
91
93
- 顶层页面 (从属于黑客)在 ` beforeunload ` 上添加一个处理方法:当 ` iframe ` 试图变更 ` top.location ` 时,访问者会收到询问是否离开的消息。
92
+ 顶级页面 (从属于黑客)在 ` beforeunload ` 上设置了一个用于阻止的处理程序,像这样:
94
93
95
- 如下所示:
96
94
``` js
97
95
window .onbeforeunload = function () {
98
- window .onbeforeunload = null ;
99
- return " Want to leave without learning all the secrets (he-he)?" ;
96
+ return false ;
100
97
};
101
98
```
102
99
103
- 大多数情况下,由于并不知道 iframe 的存在,访问者看到的只是顶层页面,即本来就要访问的页面,由此认为没有必要离开,所以会回答否。则 ` top.location ` 并不会变化!
100
+ 当 ` iframe ` 试图更改 ` top.location ` 时,访问者会收到一条消息,询问他们是否要离开页面。
101
+
102
+ 在大多数情况下,访问者会做出否定的回答,因为他们并不知道还有这么一个 iframe,他们所看到的只有顶级页面,他们没有理由离开。所以 ` top.location ` 不会变化!
104
103
105
- 作用如下 :
104
+ 演示示例 :
106
105
107
106
[ codetabs src="top-location"]
108
107
109
- ### 沙箱属性
108
+ ### Sandbox 特性
110
109
111
- 一个受 ` sandbox ` 属性限制的对象是导航 。沙箱化的 iframe 不能变更 ` top.location ` 。
110
+ ` sandbox ` 特性的限制之一就是导航 。沙箱化的 iframe 不能更改 ` top.location ` 。
112
111
113
- 但可以添加带有 ` sandbox="allow-scripts allow-forms" ` 的 iframe 标签 。从而放开限制,允许脚本和表单在 iframe 中执行。但 ` allow-top-navigation ` 禁止了 ` top.location ` 的变更 。
112
+ 但我们可以添加具有 ` sandbox="allow-scripts allow-forms" ` 的 iframe。从而放开限制,允许脚本和表单。但我们没添加 ` allow-top-navigation ` ,因此更改 ` top.location ` 是被禁止的 。
114
113
115
114
代码如下:
116
115
117
116
``` html
118
117
<iframe *!*sandbox =" allow-scripts allow-forms" * /!* src =" facebook.html" ></iframe >
119
118
```
120
119
121
- 当然还有其他绕过这个弱鸡防御的方法 。
120
+ 还有其他方式可以绕过这个弱鸡防御 。
122
121
123
122
## X-Frame-Options
124
123
125
- 服务端 header 字段 ` X-Frame-Options ` 能够允许或禁止 frame 内页面的显示 。
124
+ 服务器端 header ` X-Frame-Options ` 可以允许或禁止在 frame 中显示页面 。
126
125
127
- 这个 header 必须由 ** 服务端 ** 发送:若浏览器发现 ` <meta> ` 标签里有该字段,则会忽略此字段。即 ,` <meta http-equiv="X-Frame-Options"...> ` 不生效 。
126
+ 它必须被完全作为 HTTP- header 发送:如果浏览器在 HTML ` <meta> ` 标签中找到它,则会忽略它。因此 ,` <meta http-equiv="X-Frame-Options"...> ` 没有任何作用 。
128
127
129
- 该 header 有三个值 :
128
+ 这个 header 可能包含 3 个值 :
130
129
131
130
132
131
` DENY `
133
- : 始终禁止 frame 中的页面加载 。
132
+ : 始终禁止在 frame 中显示此页面 。
134
133
135
134
` SAMEORIGIN `
136
- : 允许和父页面同一来源的 frame 进行页面加载 。
135
+ : 允许在和父文档同源的 frame 中显示此页面 。
137
136
138
137
` ALLOW-FROM domain `
139
- : 允许和父页面同一给定域的 frame 进行页面加载 。
138
+ : 允许在来自给定域的父文档的 frame 中显示此页面 。
140
139
141
140
例如,Twitter 使用的是 ` X-Frame-Options: SAMEORIGIN ` 。
142
141
143
142
```` online
144
- 如下所示 :
143
+ 结果如下 :
145
144
146
145
```html
147
146
<iframe src="https://twitter.com"></iframe>
148
147
```
149
148
149
+ <!-- ebook: prerender/ chrome headless dies and timeouts on this iframe -->
150
150
<iframe src="https://twitter.com"></iframe>
151
151
152
- 取决于浏览器行为,以上 `iframe` 要么显示为空,要么提醒你浏览器不允许内部页面加载 。
152
+ 上面这个 `iframe` 可能为空,或者通过 alert 告知你浏览器不允许以这种方式导航至该页面,这取决于你的浏览器 。
153
153
````
154
154
155
- ## 显示不可用功能
155
+ ## 显示禁用的功能
156
156
157
- ` X-Frame-Options ` 存在副作用。它无差别地禁止合法站点在 frame 中显示我们的网页 。
157
+ ` X-Frame-Options ` 有一个副作用。其他的网站即使有充分的理由也无法在 frame 中显示我们的页面 。
158
158
159
- 所以还有其他措施... 例如,把设置了 ` height: 100%; width: 100%; ` 的 ` <div> ` “覆盖” 在页面上,这样就能监听所有的点击事件。在 ` window == top ` 或无需防御的情况下,此 ` <div> ` 则应该隐藏起来 。
159
+ 因此,还有其他解决方案…… 例如,我们可以用一个样式为 ` height: 100%; width: 100%; ` 的 ` <div> ` “覆盖”页面,这样它就能拦截所有点击。如果 ` window == top ` 或者我们确定不需要保护时,再将该 ` <div> ` 移除 。
160
160
161
- 代码示例如下 :
161
+ 像这样 :
162
162
163
163
``` html
164
164
<style >
@@ -173,31 +173,49 @@ window.onbeforeunload = function() {
173
173
</style >
174
174
175
175
<div id =" protector" >
176
- <a href =" /" target =" _blank" >Go to the site </a >
176
+ <a href =" /" target =" _blank" >前往网站 </a >
177
177
</div >
178
178
179
179
<script >
180
- // 如果顶层 window 来自不同的域,会报错
181
- // 但是此处并没有报错
180
+ // 如果顶级窗口来自其他源,这里则会出现一个 error
181
+ // 但是在本例中没有问题
182
182
if (top .document .domain == document .domain ) {
183
183
protector .remove ();
184
184
}
185
185
</script >
186
186
```
187
187
188
- 演示如下 :
188
+ 演示示例 :
189
189
190
190
[ codetabs src="protector"]
191
191
192
+ ## Samesite cookie 特性
193
+
194
+ ` samesite ` cookie 特性也可以阻止点击劫持攻击。
195
+
196
+ 具有 ` samesite ` 特性的 cookie 仅在网站是通过直接方式打开(而不是通过 frame 或其他方式)的情况下才发送到网站。更多细节请见 < info:cookie#samesite > 。
197
+
198
+ 如果网站,例如 Facebook,在其身份验证 cookie 中具有 ` samesite ` 特性,像这样:
199
+
200
+ ```
201
+ Set-Cookie: authorization=secret; samesite
202
+ ```
203
+
204
+ ……那么,当在另一个网站中的 iframe 中打开 Facebook 时,此类 cookie 将不会被发送。因此,攻击将失败。
205
+
206
+ 当不实用 cookie 时,` samesite ` cookie 特性将不会有任何影响。这可以使其他网站能够轻松地在 iframe 中显示我们公开的、未进行身份认证的页面。
207
+
208
+ 然而,这也可能会使得劫持攻击在少数情况下起作用。例如,通过检查 IP 地址来防止重复投票的匿名投票网站仍然会受到点击劫持的攻击,因为它不使用 cookie 对用户身份进行验证。
209
+
192
210
## 总结
193
211
194
- 点击劫持是一种 “欺骗” 用户在不知情下点击恶意站点的方式 。如果是重要的点击操作,这是非常危险的。
212
+ 点击劫持是一种“诱骗”用户在不知情的情况下点击恶意网站的方式 。如果是重要的点击操作,这是非常危险的。
195
213
196
- 黑客可以通过信息提交一个链接到他的恶意网页,或者通过某些手段引诱访问者访问他的网页。当然还有许多其他变体 。
214
+ 黑客可以通过信息发布指向他的恶意页面的链接,或者通过某些手段引诱访问者访问他的页面。当然还有很多其他变体 。
197
215
198
- 一方面 —— 这种攻击方式是“浅层”的:黑客只需要拦截一次点击 。但另一方面,如果被这次点击之后会开启另一个控制开关,那么黑客同样用狡猾的提示强制用户点击这些控制按钮 。
216
+ 一方面 —— 这种攻击方式是“浅层”的:黑客所做的只是拦截一次点击 。但另一方面,如果黑客知道在点击之后将出现另一个控件,则他们可能还会使用狡猾的消息来迫使用户也点击它们 。
199
217
200
- 这种攻击相当危险,因为在设计交互界面时,通常不会考虑到可能会有黑客代替真正的访问者点击界面。所以许多意想不到的地方可能发现攻击漏洞 。
218
+ 这种攻击相当危险,因为在设计交互界面时,我们通常不会考虑到可能会有黑客代表用户点击界面。所以,在许多意想不到的地方可能发现攻击漏洞 。
201
219
202
- - 推荐在网页上(或整个站点 )使用 ` X-Frame-Options: SAMEORIGIN ` ,这不会被 frame 内部读取 。
203
- - 若要允许的页面在 frame 中显示,用一个 ` <div> ` 遮盖,这样仍然是安全的 。
220
+ - 建议在那些不希望被在 frame 中查看的页面上(或整个网站上 )使用 ` X-Frame-Options: SAMEORIGIN ` 。
221
+ - 如果我们希望允许在 frame 中显示我们的页面,那我们使用一个 ` <div> ` 对整个页面进行遮盖,这样也是安全的 。
0 commit comments