Skip to content

Update translation of 3-frames-and-windows/06-clickjacking #719

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Apr 8, 2020
2 changes: 1 addition & 1 deletion 1-js/01-getting-started/3-code-editors/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

[IDE](https://en.wikipedia.org/wiki/Integrated_development_environment)(集成开发环境)是用于管理整个项目具有强大功能的编辑器。顾名思义,它不仅仅是一个编辑器,而且还是个完整的开发环境。

IDE 加载项目(通常包含很多文件),并且允许在不同文件之间切换。IDE 还提供基于整个项目(不仅仅是打开的文件)的自动补全功能,集成版本控制(如 [git](https://git-scm.com/))、集成测试环境等一些其他“项目层面”的东西。
IDE 加载项目(通常包含很多文件),并且允许在不同文件之间导航(navigation)。IDE 还提供基于整个项目(不仅仅是打开的文件)的自动补全功能,集成版本控制(如 [git](https://git-scm.com/))、集成测试环境等一些其他“项目层面”的东西。

如果你还没考虑好选哪一款 IDE,可以考虑下面两个:

Expand Down
2 changes: 1 addition & 1 deletion 1-js/03-code-quality/01-debugging-chrome/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

资源(Sources)面板包含三个部分:

1. **文件浏览(File Navigator)** 区域列出了 HTML、JavaScript、CSS 和包括图片在内的其他依附于此页面的文件。Chrome 扩展程序也会显示在这。
1. **文件导航(File Navigator)** 区域列出了 HTML、JavaScript、CSS 和包括图片在内的其他依附于此页面的文件。Chrome 扩展程序也会显示在这。
2. **代码编辑(Code Editor)** 区域展示源码。
3. **JavaScript 调试(JavaScript Debugging)** 区域是用于调试的,我们很快就会来探索它。

Expand Down
8 changes: 4 additions & 4 deletions 1-js/05-data-types/06-iterable/article.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@

# Iterable(可迭代对象)
# Iterable object(可迭代对象)

**Iterable**(可迭代对象)是数组的泛化。这个概念是说任何对象都可以被定制为可在 `for..of` 循环中使用的对象。
**可迭代(Iterable)** 对象是数组的泛化。这个概念是说任何对象都可以被定制为可在 `for..of` 循环中使用的对象。

数组本身就是可迭代的。但不仅仅是数组。很多其他内建对象也都可以迭代,例如字符串也是可以迭代的
数组是可迭代的。但不仅仅是数组。很多其他内建对象也都是可迭代的。例如字符串也是可迭代的

如果从技术上讲,对象不是数组,而是表示某物的集合(列表,集合),`for..of` 是一个能够遍历它的很好的语法,因此,让我们看看如何使其工作
如果从技术上讲,对象不是数组,而是表示某物的集合(列表,集合),`for..of` 是一个能够遍历它的很好的语法,因此,让我们来看看如何使其发挥作用


## Symbol.iterator
Expand Down
2 changes: 1 addition & 1 deletion 1-js/13-modules/02-import-export/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ say.*!*bye*/!*('John'); // Bye, John!

大部分情况下,开发者倾向于使用第二种方式,以便每个“东西”都存在于它自己的模块中。

当然,这需要大量文件,因为每个东西都需要自己的模块,但这根本不是问题。实际上,如果文件具有良好的命名,并且文件夹结构得当,那么代码导航会变得更容易
当然,这需要大量文件,因为每个东西都需要自己的模块,但这根本不是问题。实际上,如果文件具有良好的命名,并且文件夹结构得当,那么代码导航(navigation)会变得更容易

模块提供了特殊的默认导出 `export default` 语法,以使“一个模块只做一件事”的方式看起来更好。

Expand Down
136 changes: 77 additions & 59 deletions 3-frames-and-windows/06-clickjacking/article.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# 点击劫持攻击

“点击劫持” 攻击即允许恶意网页**以用户的名义**点击 “受害站点”。
“点击劫持”攻击允许恶意页面 **以用户的名义** 点击“受害网站”。

许多站点都被这样攻击过,包括 Twitter、Facebook 和 Paypal 等等许多网站。当然,目前它们都已修复这个问题
许多网站都被黑客以这种方式攻击过,包括 Twitter、Facebook 和 Paypal 等许多网站。当然,它们都已经被修复了

## 原理

原理十分简单。

以下以 Facebook 为例解释点击劫持是如何运作的
我们以 Facebook 为例,解释点击劫持是如何完成的

1. 访问者被恶意网页吸引。此处略过如何被吸引的
2. 页面上存在一个看起来无害的链接(比如:“马上有钱” 或者 “点我,超好玩!”)。
3. 恶意网页在该链接之上放置一个透明 `<iframe>` 标签,其中 `src` 指向 facebook.com,如此一来,“点赞” 按钮恰好在链接上面。通常用 `z-index` 实现
4. 如果用户试图点击该链接,实际上是点到了 “点赞” 按钮上
1. 访问者被恶意页面吸引。怎样吸引的不重要
2. 页面上有一个看起来无害的链接(例如:“变得富有”或者“点我,超好玩!”)。
3. 恶意页面在该链接上方放置了一个透明的 `<iframe>`,其 `src` 来自于 facebook.com,这使得“点赞”按钮恰好位于该链接上面。这通常是通过 `z-index` 实现的
4. 用户尝试点击该链接时,实际上点击的是“点赞”按钮

## 示例

以下是恶意网页的一般代码。为了更好的说明问题,`<iframe>` 标签设置成半透明状态(真正的恶意网页为全透明状态):
这是恶意页面看起来的样子。为了清楚起见,我们将 `<iframe>` 设置成了半透明的(在真正的恶意页面中,它是全透明的):

```html run height=120 no-beautify
<style>
Expand All @@ -27,138 +27,138 @@ iframe { /* 来自受害网站的 iframe */
position: absolute;
top:0; left:-20px;
*!*
opacity: 0.5; /* 真实为 opacity:0 */
opacity: 0.5; /* 在实际中为 opacity:0 */
*/!*
z-index: 1;
}
</style>

<div>马上有钱:</div>
<div>点击即可变得富有:</div>

<!-- 来自受害网站的 url -->
*!*
<iframe src="/clickjacking/facebook.html"></iframe>

<button>点我!点我!</button>
<button>点这里!</button>
*/!*

<div>...你会变帅(我才是帅黑客😜)!</div>
<div>……你很酷(我实际上是一名帅气的黑客)!</div>
```

完整的攻击示例如下:

[codetabs src="clickjacking-visible" height=160]

例子中的半透明 `<iframe src="facebook.html">` 覆盖在按钮之上。点击按钮实际上点击在 iframe 标签上,但由于 iframe 标签透明,这一动作对用户不可见
在上面这个示例中,我们有一个半透明的 `<iframe src="facebook.html">`,我们可以看到,它位于按钮之上。点击按钮实际上会点击在 iframe 上,但这对用户不可见,因为 iframe 是透明的

结果,如果访问者登陆了 Facebook(“记住我”通常是打开的),那么这个行为就会点一个“赞”。Twitter 上是 "Follow" 按钮。

因此,若访问者曾登陆 Facebook(“记住我” 开关打开),这个动作会使用户在 Facebook 上进行 “Like” 操作。Twitter 上是 “Follow” 操作。

下面的例子相同,但 `iframe` 设置为 `opacity:0` 更符合实际情况:
下面是相同的示例,但 `iframe` 的透明度设置为了 `opacity:0`,更符合实际情况:

[codetabs src="clickjacking" height=160]

只需要在恶意网页中的链接正上方放置 `<iframe>`,点击按钮就能发起攻击。通常用 CSS 就能实现
我们进行攻击所需要做的 —— 就是将 `<iframe>` 放置在恶意页面中,使得按钮恰好位于链接的正上方。这样当用户点击链接时,他们实际上点击的是按钮。这通常可以通过 CSS 实现

```smart header="点击劫持作用于点击事件,而非键盘事件"
此攻击仅影响鼠标操作
```smart header="点击劫持是对点击事件,而非键盘事件"
此攻击仅影响鼠标行为(或者类似的行为,例如在手机上的点击)

从技术上讲,可以用 iframe 中的文本域覆盖原有的文本域实现攻击。所以当访问者试图聚焦网页中的 input 标签时,实际上聚焦的是 iframe 中的 input 标签
键盘输入很难重定向。从技术上讲,我们可以用 iframe 的文本区域覆盖原有的文本区域实现攻击。因此,当访问者试图聚焦页面中的输入时,实际上聚焦的是 iframe 中的输入

但是这里有个问题。访问者的所有输入都会被隐藏,因为该 iframe 是不可见的。
但是这里有个问题。访问者键入的所有内容都会被隐藏,因为该 iframe 是不可见的。

当用户无法在屏幕上看到自己输入的字符时,通常会停止打字。
```

## 传统防御(弱 👎)

最古老的防御是一段禁止在非顶层页面中打开网页的 JavaScript 代码(所谓的 “framebusting”)。
最古老的防御措施是一段用于禁止在 frame 中打开页面的 JavaScript 代码(所谓的 “framebusting”)。

如下所示
它看起来像这样

```js
if (top != window) {
top.location = window.location;
}
```

意思是:window 强制置顶,如果没在顶层,自动置顶
意思是说:如果 window 发现它不在顶部,那么它将自动使其自身位于顶部

这个方法并不可靠,因为有许多方式可以绕过这个限制。下面就介绍几个
这个方法并不可靠,因为有许多方式可以绕过这个限制。下面我们就介绍几个

### 阻塞顶层容器
### 阻止顶级导航

[beforeunload](info:onload-ondomcontentloaded#window.onbeforeunload) 事件中阻塞 `top.location` 变更过渡
我们可以阻止因更改 [beforeunload](info:onload-ondomcontentloaded#window.onbeforeunload) 事件处理程序中的 `top.location` 而引起的过渡(transition)

顶层页面(从属于黑客)在 `beforeunload` 上添加一个处理方法:当 `iframe` 试图变更 `top.location` 时,访问者会收到询问是否离开的消息。
顶级页面(从属于黑客)在 `beforeunload` 上设置了一个用于阻止的处理程序,像这样:

如下所示:
```js
window.onbeforeunload = function() {
window.onbeforeunload = null;
return "Want to leave without learning all the secrets (he-he)?";
return false;
};
```

大多数情况下,由于并不知道 iframe 的存在,访问者看到的只是顶层页面,即本来就要访问的页面,由此认为没有必要离开,所以会回答否。则 `top.location` 并不会变化!
当 `iframe` 试图更改 `top.location` 时,访问者会收到一条消息,询问他们是否要离开页面。

在大多数情况下,访问者会做出否定的回答,因为他们并不知道还有这么一个 iframe,他们所看到的只有顶级页面,他们没有理由离开。所以 `top.location` 不会变化!

作用如下
演示示例

[codetabs src="top-location"]

### 沙箱属性
### Sandbox 特性

一个受 `sandbox` 属性限制的对象是导航。沙箱化的 iframe 不能变更 `top.location`。
`sandbox` 特性的限制之一就是导航。沙箱化的 iframe 不能更改 `top.location`。

但可以添加带有 `sandbox="allow-scripts allow-forms"` 的 iframe 标签。从而放开限制,允许脚本和表单在 iframe 中执行。但 `allow-top-navigation` 禁止了 `top.location` 的变更
但我们可以添加具有 `sandbox="allow-scripts allow-forms"` 的 iframe。从而放开限制,允许脚本和表单。但我们没添加 `allow-top-navigation`,因此更改 `top.location` 是被禁止的

代码如下:

```html
<iframe *!*sandbox="allow-scripts allow-forms"*/!* src="facebook.html"></iframe>
```

当然还有其他绕过这个弱鸡防御的方法
还有其他方式可以绕过这个弱鸡防御

## X-Frame-Options

服务端 header 字段 `X-Frame-Options` 能够允许或禁止 frame 内页面的显示
服务器端 header `X-Frame-Options` 可以允许或禁止在 frame 中显示页面

这个 header 必须由 **服务端** 发送:若浏览器发现 `<meta>` 标签里有该字段,则会忽略此字段。即,`<meta http-equiv="X-Frame-Options"...>` 不生效
它必须被完全作为 HTTP-header 发送:如果浏览器在 HTML `<meta>` 标签中找到它,则会忽略它。因此,`<meta http-equiv="X-Frame-Options"...>` 没有任何作用

header 有三个值
这个 header 可能包含 3 个值


`DENY`
: 始终禁止 frame 中的页面加载
: 始终禁止在 frame 中显示此页面

`SAMEORIGIN`
: 允许和父页面同一来源的 frame 进行页面加载
: 允许在和父文档同源的 frame 中显示此页面

`ALLOW-FROM domain`
: 允许和父页面同一给定域的 frame 进行页面加载
: 允许在来自给定域的父文档的 frame 中显示此页面

例如,Twitter 使用的是 `X-Frame-Options: SAMEORIGIN`。

````online
如下所示
结果如下

```html
<iframe src="https://twitter.com"></iframe>
```

<!-- ebook: prerender/ chrome headless dies and timeouts on this iframe -->
<iframe src="https://twitter.com"></iframe>

取决于浏览器行为,以上 `iframe` 要么显示为空,要么提醒你浏览器不允许内部页面加载
上面这个 `iframe` 可能为空,或者通过 alert 告知你浏览器不允许以这种方式导航至该页面,这取决于你的浏览器
````

## 显示不可用功能
## 显示禁用的功能

`X-Frame-Options` 存在副作用。它无差别地禁止合法站点在 frame 中显示我们的网页
`X-Frame-Options` 有一个副作用。其他的网站即使有充分的理由也无法在 frame 中显示我们的页面

所以还有其他措施...例如,把设置了 `height: 100%; width: 100%;` 的 `<div>` “覆盖” 在页面上,这样就能监听所有的点击事件。在 `window == top` 或无需防御的情况下,此 `<div>` 则应该隐藏起来
因此,还有其他解决方案……例如,我们可以用一个样式为 `height: 100%; width: 100%;` 的 `<div>` “覆盖”页面,这样它就能拦截所有点击。如果 `window == top` 或者我们确定不需要保护时,再将该 `<div>` 移除

代码示例如下
像这样

```html
<style>
Expand All @@ -173,31 +173,49 @@ window.onbeforeunload = function() {
</style>

<div id="protector">
<a href="/" target="_blank">Go to the site</a>
<a href="/" target="_blank">前往网站</a>
</div>

<script>
// 如果顶层 window 来自不同的域,会报错
// 但是此处并没有报错
// 如果顶级窗口来自其他源,这里则会出现一个 error
// 但是在本例中没有问题
if (top.document.domain == document.domain) {
protector.remove();
}
</script>
```

演示如下
演示示例

[codetabs src="protector"]

## Samesite cookie 特性

`samesite` cookie 特性也可以阻止点击劫持攻击。

具有 `samesite` 特性的 cookie 仅在网站是通过直接方式打开(而不是通过 frame 或其他方式)的情况下才发送到网站。更多细节请见 <info:cookie#samesite>。

如果网站,例如 Facebook,在其身份验证 cookie 中具有 `samesite` 特性,像这样:

```
Set-Cookie: authorization=secret; samesite
```

……那么,当在另一个网站中的 iframe 中打开 Facebook 时,此类 cookie 将不会被发送。因此,攻击将失败。

当不实用 cookie 时,`samesite` cookie 特性将不会有任何影响。这可以使其他网站能够轻松地在 iframe 中显示我们公开的、未进行身份认证的页面。

然而,这也可能会使得劫持攻击在少数情况下起作用。例如,通过检查 IP 地址来防止重复投票的匿名投票网站仍然会受到点击劫持的攻击,因为它不使用 cookie 对用户身份进行验证。

## 总结

点击劫持是一种 “欺骗” 用户在不知情下点击恶意站点的方式。如果是重要的点击操作,这是非常危险的。
点击劫持是一种“诱骗”用户在不知情的情况下点击恶意网站的方式。如果是重要的点击操作,这是非常危险的。

黑客可以通过信息提交一个链接到他的恶意网页,或者通过某些手段引诱访问者访问他的网页。当然还有许多其他变体
黑客可以通过信息发布指向他的恶意页面的链接,或者通过某些手段引诱访问者访问他的页面。当然还有很多其他变体

一方面 —— 这种攻击方式是“浅层”的:黑客只需要拦截一次点击。但另一方面,如果被这次点击之后会开启另一个控制开关,那么黑客同样用狡猾的提示强制用户点击这些控制按钮
一方面 —— 这种攻击方式是“浅层”的:黑客所做的只是拦截一次点击。但另一方面,如果黑客知道在点击之后将出现另一个控件,则他们可能还会使用狡猾的消息来迫使用户也点击它们

这种攻击相当危险,因为在设计交互界面时,通常不会考虑到可能会有黑客代替真正的访问者点击界面。所以许多意想不到的地方可能发现攻击漏洞
这种攻击相当危险,因为在设计交互界面时,我们通常不会考虑到可能会有黑客代表用户点击界面。所以,在许多意想不到的地方可能发现攻击漏洞

- 推荐在网页上(或整个站点)使用 `X-Frame-Options: SAMEORIGIN`,这不会被 frame 内部读取
- 若要允许的页面在 frame 中显示,用一个 `<div>` 遮盖,这样仍然是安全的
- 建议在那些不希望被在 frame 中查看的页面上(或整个网站上)使用 `X-Frame-Options: SAMEORIGIN`。
- 如果我们希望允许在 frame 中显示我们的页面,那我们使用一个 `<div>` 对整个页面进行遮盖,这样也是安全的
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
}
</style>

<div>Click to get rich now:</div>
<div>点击即可变得富有:</div>

<!-- The url from the victim site -->
<!-- 来自受害网站的 url -->
<iframe src="facebook.html"></iframe>

<button>Click here!</button>
<button>点这里!</button>

<div>...And you're cool (I'm a cool hacker actually)!</div>
<div>……你很酷(我实际上是一名帅气的黑客)!</div>

</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
}
</style>

<div>Click to get rich now:</div>
<div>点击即可变得富有:</div>

<!-- The url from the victim site -->
<!-- 来自受害网站的 url -->
<iframe src="facebook.html"></iframe>

<button>Click here!</button>
<button>点这里!</button>

<div>...And you're cool (I'm a cool hacker actually)!</div>
<div>……你很酷(我实际上是一名帅气的黑客)!</div>

</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<body>

<div id="protector">
<a href="/" target="_blank">Go to the site</a>
<a href="/" target="_blank">前往网站</a>
</div>

<script>
Expand All @@ -31,11 +31,11 @@

</script>

This text is always visible.
文本一直是可见的。

But if the page was open inside a document from another domain, the div over it would prevent any actions.
但是,如果该页面是在来自另一个域的文档中打开的,则该页面上的 div 将阻止所有行为。

<button onclick="alert(1)">Click wouldn't work in that case</button>
<button onclick="alert(1)">在这种情况下,点击不起作用</button>

</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@

<body>

<p>After a click on the button the visitor gets a "strange" question about whether they want to leave.</p>
<p>点击该按钮后,访问者会收到一条关于他们是否要离开的“奇怪”问题。</p>

<p>Probably they would respond "No", and the iframe protection is hacked.</p>
<p>他们可能会回答“否”,这样就保护了 iframe 不被黑。</p>

<button onclick="attack()">Add a "protected" iframe</button>
<button onclick="attack()">添加一个“受保护的” iframe</button>

</body>
</html>