diff --git a/6-data-storage/01-cookie/article.md b/6-data-storage/01-cookie/article.md index 2d1161e344..a8cde54a48 100644 --- a/6-data-storage/01-cookie/article.md +++ b/6-data-storage/01-cookie/article.md @@ -1,80 +1,80 @@ # Cookies, document.cookie -Cookies are small strings of data that are stored directly in the browser. They are a part of HTTP protocol, defined by [RFC 6265](https://tools.ietf.org/html/rfc6265) specification. +cookies 是直接保存在浏览器上的小数据串。它们是 HTTP 协议的一部分,由 [RFC 6265](https://tools.ietf.org/html/rfc6265) 规范定义。 -Most of the time, cookies are set by a web server. Then they are automatically added to every request to the same domain. +大多数情况下,cookies 是由 web 服务器设置的。然后它们会自动添加到相同域名下的每次请求中。 -One of the most widespread use cases is authentication: +最常见的用处之一是身份验证: -1. Upon sign in, the server uses `Set-Cookie` HTTP-header in the response to set a cookie with "session identifier". -2. Next time when the request is set to the same domain, the browser sends the over the net using `Cookie` HTTP-header. -3. So the server knows who made the request. +1. 登录后,服务端通过 `Set-Cookie` 在响应的 HTTP-header 中设置了一个带有 "会话标识符" 的 cookie。 +2. 下次如果相同域名发起了请求,浏览器会发送带有 `Cookie` 的 HTTP-header。 +3. 所以服务端知道是谁发起了请求。 -We can also access cookies from the browser, using `document.cookie` property. +我们还可以使用 `document.cookie` 属性在浏览器上访问 cookies。 -There are many tricky things about cookies and their options. In this chapter we'll cover them in detail. +有关 cookies 和它们的选项有很多棘手的事情。在本章节中,我们将会详细介绍。 -## Reading from document.cookie +## 从 document.cookie 中读取 ```online -Do you have any cookies on this site? Let's see: +你在这个网站上有 cookies 吗?让我们来看看: ``` ```offline -Assuming you're on a website, it's possible to see the cookies, like this: +假设你在网站上可以看到 cookies,像这样: ``` ```js run -// At javascript.info, we use Google Analytics for statistics, -// so there should be some cookies +// 在 javascript.info,我们使用谷歌分析来统计, +// 所以应该存在一些 cookies alert( document.cookie ); // cookie1=value1; cookie2=value2;... ``` -The value of `document.cookie` consists of `name=value` pairs, delimited by `; `. Each one is a separate cookie. +`document.cookie` 的值由一个个 `name=value` 组成,以 `; ` 相隔。每一个都是独立的 cookie。 -To find a particular cookie, we can split `document.cookie` by `; `, and then find the right name. We can use either a regular expression or array functions to do that. +为了找到一个特定的 cookie,我们可以通过 `; ` 截取 `document.cookie`,然后找到合适的名字。我们可以使用正则表达式或者数组的方法来实现。 -We leave it as an exercise for the reader. Also, at the end of the chapter you'll find helper functions to manipulate cookies. +我们把这个留给读者当作练习。此外,在本章节的结尾,你可以找到一些操作 cookies 的辅助函数。 -## Writing to document.cookie +## 写入 document.cookie -We can write to `document.cookie`. But it's not a data property, it's an accessor. +我们可以写入 `document.cookie`。但是这不是一个数据属性,它是一个访问者。 -**A write operation to `document.cookie` passes through the browser that updates cookies mentioned in it, but doesn't touch other cookies.** +**浏览器的 `document.cookie` 写入操作只会更新已存在的 cookies,而不会影响其他 cookies。** -For instance, this call sets a cookie with the name `user` and value `John`: +例如,这里设置了一个名称为 `user` 和值为 `John` 的 cookie: ```js run -document.cookie = "user=John"; // update only cookie named 'user' -alert(document.cookie); // show all cookies +document.cookie = "user=John"; // 只会更新名称为 user 的 cookie +alert(document.cookie); // 展示所有 cookies ``` -If you run it, then probably you'll see multiple cookies. That's because `document.cookie=` operation does not overwrite all cookies. It only sets the mentioned cookie `user`. +如果你运行了代码,你很可能会看到多个 cookies。这是因为 `document.cookie=` 操作不是重写整个 cookies。它只设置代码中提到的 cookie `user`。 -Technically, name and value can have any characters, but to keep the formatting valid they should be escaped using a built-in `encodeURIComponent` function: +从技术层面看,cookie 的名称和值会有很多种类型,但是为了保持格式有效,它们应该使用 `encodeURIComponent` 内置方法来编码一下: ```js run -// special values, need encoding +// 特殊值,需要编码 let name = "my name"; let value = "John Smith" -// encodes the cookie as my%20name=John%20Smith +// 编码后变成 my%20name=John%20Smith document.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value); alert(document.cookie); // ...; my%20name=John%20Smith ``` -```warn header="Limitations" -There are few limitations: -- The `name=value` pair, after `encodeURIComponent`, should not exceed 4kb. So we can't store anything huge in a cookie. -- The total number of cookies per domain is limited to around 20+, the exact limit depends on a browser. +```warn header="局限性" +存在一些局限性: +- `encodeURIComponent` 编码后的 `name=value` 对,大小不能超过 4kb。所以我们不能在一个 cookie 中保存大数据。 +- 每个域名下所有 cookies 的总数限制在 20 几个,实际的限制数量取决于浏览器。 ``` -Cookies have several options, many of them are important and should be set. +cookies 有好几个选项,很多选项都很重要并且应该设置它。 -The options are listed after `key=value`, delimited by `;`, like this: +选项列在 `key=value` 后面,使用 `;` 间隔,像这样: ```js run document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT" @@ -84,80 +84,80 @@ document.cookie = "user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT" - **`path=/mypath`** -The url path prefix, where the cookie is accessible. Must be absolute. By default, it's the current path. +可访问到 cookie 的 url 路径前缀。必须是绝对路径。默认值为当前路径。 -If a cookie is set with `path=/admin`, it's visible at pages `/admin` and `/admin/something`, but not at `/home` or `/adminpage`. +如果一个 cookie 设置了 `path=/admin`,那么在 `/admin` 和 `/admin/something` 下都是可见的,但是在 `/home` 或 `/adminpage` 下不可见。 -Usually, we set `path=/` to make the cookie accessible from all website pages. +通常,我们设置 `path=/` 来允许网站下所有页面访问 cookie。 ## domain - **`domain=site.com`** -A domain where the cookie is accessible. In practice though, there are limitations. We can't set any domain. +可访问到 cookie 的域名。但是在实践中,存在局限性。我们不能设置任何域名。 -By default, a cookie is accessible only at the domain that set it. So, if the cookie was set by `site.com`, we won't get it `other.com`. +默认情况下,cookie 只能在设置的域名下才能访问到。所以,如果 cookie 设置在 `site.com` 下,我们不能在任何其他域名下(`other.com`)访问它。 -...But what's more tricky, we also won't get the cookie at a subdomain `forum.site.com`! +……但是棘手的是,我们在子域名下同样不能获取到 cookie(`forum.site.com`)! ```js -// at site.com +// 在 site.com document.cookie = "user=John" -// at forum.site.com -alert(document.cookie); // no user +// 在 forum.site.com +alert(document.cookie); // 没有 user ``` -**There's no way to let a cookie be accessible from another 2nd-level domain, so `other.com` will never receive a cookie set at `site.com`.** +**让 cookie 在另外一个二级域名下可以访问到是没有办法的,所以其他域名 `other.com` 将不会接收到设置在 `site.com` 的 cookie。** -It's a safety restriction, to allow us to store sensitive data in cookies. +这是一个安全限制,为了允许我们可以在 cookie 中保存敏感信息。 -...But if we'd like to grant access to subdomains like `forum.site.com`, that's possible. We should explicitly set `domain` option to the root domain: `domain=site.com`: +……但是如果我们想要批准像 `forum.site.com` 这样的子域名访问,这是可以做到的。我们应该明确设置 `domain` 选项为根域名:`domain=site.com`: ```js -// at site.com, make the cookie accessible on any subdomain: +// 在 site.com 中, 设置 cookie 在任何子域名下可以访问: document.cookie = "user=John; domain=site.com" -// at forum.site.com -alert(document.cookie); // with user +// 在 forum.site.com +alert(document.cookie); // 也存在 user ``` -For historical reasons, `domain=.site.com` (a dot at the start) also works this way, it might better to add the dot to support very old browsers. +因为历史原因,`domain=.site.com`(以点开头)也可以正常使用,最好添加点来支持老版本的浏览器。 -So, `domain` option allows to make a cookie accessible at subdomains. +所以,`domain` 选项允许子域名访问 cookie。 ## expires, max-age -By default, if a cookie doesn't have one of these options, it disappears when the browser is closed. Such cookies are called "session cookies" +默认情况下,如果一个 cookie 没有设置这两个参数中的任何一个,那么在浏览器关闭后,它就会消失。此类 cookies 被称为 "session cookies”。 -To let cookies survive browser close, we can set either `expires` or `max-age` option. +为了让 cookies 在浏览器关闭后仍然存在,我们可以设置 `expires` 或 `max-age` 其中一个选项。 - **`expires=Tue, 19 Jan 2038 03:14:07 GMT`** -Cookie expiration date, when the browser will delete it automatically. +cookie 过期日期,当到了过期时间浏览器会自动删除它。 -The date must be exactly in this format, in GMT timezone. We can use `date.toUTCString` to get it. For instance, we can set the cookie to expire in 1 day: +日期必须是这种格式,GMT 时区。我们可以使用 `date.toUTCString` 方法得到。举个例子,我们可以设置 cookie 在 1 天后过期。 ```js -// +1 day from now +// 在当前时间上加 1 天 let date = new Date(Date.now() + 86400e3); date = date.toUTCString(); document.cookie = "user=John; expires=" + date; ``` -If we set `expires` to a date in the past, the cookie is deleted. +如果我们设置 `expires` 为已经过去的时间,cookie 会被删除。 - **`max-age=3600`** -An alternative to `expires`, specifies the cookie expiration in seconds from the current moment. +一个可以替代 `expires` 的选项,具体说明 cookie 的过期时间距离当前时间的秒数。 -If zero or negative, then the cookie is deleted: +如果是 0 或者负数,cookie 会被删除: ```js -// cookie will die +1 hour from now +// 1 小时后 cookie 会失效 document.cookie = "user=John; max-age=3600"; -// delete cookie (let it expire right now) +// 删除 cookie (让 cookie 马上过期) document.cookie = "user=John; max-age=0"; ``` @@ -165,123 +165,123 @@ document.cookie = "user=John; max-age=0"; - **`secure`** -The cookie should be transferred only over HTTPS. +cookie 应仅在 HTTPS 环境下传输。 -**By default, if we set a cookie at `http://site.com`, then it also appears at `https://site.com` and vice versa.** +**默认情况下,如果我们在 `http://site.com` 设置了 cookie,然后 cookie 在 `https://site.com` 中也会出现,反之亦然。** -That is, cookies are domain-based, they do not distinguish between the protocols. +也就是说,cookies 是基于域名的,它们不是通过协议来区分的。 -With this option, if a cookie is set by `https://site.com`, then it doesn't appear when the same site is accessed by HTTP, as `http://site.com`. So if a cookie has sensitive content that should never be sent over unencrypted HTTP, then the flag is the right thing. +有了这个选项,如果一个 cookie 通过 `https://site.com` 设置,然后它不会在相同域名的 HTTP 环境下出现,例如 `http://site.com`。所以,如果一个 cookie 存有敏感内容,不应该在不安全的 HTTP 环境下发送,此时这个选项就派上用场了。 ```js -// assuming we're on https:// now -// set the cookie secure (only accessible if over HTTPS) +// 假设我们现在在 HTTPS 环境下 +// 设置 cookie secure(只在 HTTPS 环境下传输) document.cookie = "user=John; secure"; ``` ## samesite -That's another security option, to protect from so-called XSRF (cross-site request forgery) attacks. +这是另外一个关于安全的选项,为了防止 XSRF(跨站点请求伪造)攻击。 -To understand when it's useful, let's introduce the following attack scenario. +为了理解它什么时候起效,我们来介绍下以下的攻击情况。 -### XSRF attack +### XSRF 攻击 -Imagine, you are logged into the site `bank.com`. That is: you have an authentication cookie from that site. Your browser sends it to `bank.com` with every request, so that it recognizes you and performs all sensitive financial operations. +想象一下,你登录了 `bank.com` 网站。此时:你有了该站点的身份验证 cookie。你的浏览器会随着每次请求把它发送到 `bank.com`,因此,`bank.com` 承认你的身份和你做出的所有敏感经济操作。 -Now, while browsing the web in another window, you occasionally come to another site `evil.com`, that automatically submits a form `
` to `bank.com` with input fields that initiate a transaction to the hacker's account. +现在,在另外一个窗口浏览网页时,你偶然访问了另外一个网站 `evil.com`,该网站自动提交了一个表单 `` 到 `bank.com`,提交的表单字段能够开始一笔到黑客账户的交易。 -The form is submitted from `evil.com` directly to the bank site, and your cookie is also sent, just because it's sent every time you visit `bank.com`. So the bank recognizes you and actually performs the payment. +表单直接从 `evil.com` 提交到银行网站,并且你的 cookie 也一起发送,仅仅因为 cookie 会在你每次访问 `bank.com` 时都会发送。所以银行识别你的身份并实际执行付款。 ![](cookie-xsrf.svg) -That's called a cross-site request forgery (or XSRF) attack. +这就被称为一个跨站点请求伪造(或XSRF)攻击。 -Real banks are protected from it of course. All forms generated by `bank.com` have a special field, so called "xsrf protection token", that an evil page can't neither generate, nor somehow extract from a remote page (it can submit a form there, but can't get the data back). +当然,真正的银行会防止出现这种情况。所有 `bank.com` 生成的表单都有一个特殊的字段,所谓的 "xsrf 保护令牌”,邪恶页面既不能生成,也不能从远程页面获取(它可以在那里提交表单,但是无法获取数据)。 -But that takes time to implement: we need to ensure that every form has the token field, and we must also check all requests. +但这需要时间来实现:我们需要确保每个表单都有 token 字段,而且必须检查所有的请求。 -### Enter cookie samesite option +### 输入 cookie samesite 选项 -The cookie `samesite` option provides another way to protect from such attacks, that (in theory) should not require "xsrf protection tokens". +cookie 的 `samesite` 选项提供了另一种防止此类攻击的方法,(理论上)应该不需要 "xsrf 保护令牌”。 -It has two possible values: +它有两个可选的值: -- **`samesite=strict` (same as `samesite` without value)** +- **`samesite=strict` (和 `samesite` 没有值一样)** -A cookie with `samesite=strict` is never sent if the user comes from outside the site. +如果用户来自外部的网站,那么设置了 `samesite=strict` 的 cookie 永远不会发送。 -In other words, whether a user follows a link from their mail or submits a form from `evil.com`, or does any operation that originates from another domain, the cookie is not sent. +换句话说,无论用户是跟踪邮件链接或从 `evil.com` 提交表单,或者来自其他域名下的任何操作,cookie 都不会发送。 -If authentication cookies have `samesite` option, then XSRF attack has no chances to succeed, because a submission from `evil.com` comes without cookies. So `bank.com` will not recognize the user and will not proceed with the payment. +如果身份验证的 cookies 存在 `samesite` 选项,XSRF 攻击是没有机会成功的,因为 `evil.com` 发起的提交没有 cookies。所以 `bank.com` 无法识别用户,并且不会继续付款。 -The protection is quite reliable. Only operations that come from `bank.com` will send the `samesite` cookie. +保护非常有效。只有来自 `bank.com` 的操作才会发送 `samesite` cookie。 -Although, there's a small inconvenience. +虽然,这样有一点点不方便。 -When a user follows a legitimate link to `bank.com`, like from their own notes, they'll be surprised that `bank.com` does not recognize them. Indeed, `samesite=strict` cookies are not sent in that case. +当用户跟随合法链接来到 `bank.com`,例如他们自己的笔记,他们会感到惊讶,`bank.com` 不能识别他们的身份。实际上,在这种情况下 `samesite=strict` cookies 不会发送。 -We could work around that by using two cookies: one for "general recognition", only for the purposes of saying: "Hello, John", and the other one for data-changing operations with `samesite=strict`. Then a person coming from outside of the site will see a welcome, but payments must be initiated from the bank website. +我们可以使用两个 cookies 来解决这个问题:一个 cookie 用来 "大致识别",仅用来说 "Hello, John",另外一个带有 `samesite=strict` 的 cookie 用来验证数据改变的操作。然后来自外部网站的用户会看到欢迎页面,但必须在银行的网站上发起付款。 - **`samesite=lax`** -A more relaxed approach that also protects from XSRF and doesn't break user experience. +一种更轻松的方法也能防止 XSRF 攻击并且不会破坏用户体验。 -Lax mode, just like `strict`, forbids the browser to send cookies when coming from outside the site, but adds an exception. +lax 模式,和 `strict` 模式类似,禁止浏览器发送来自外部网站的 cookie,但是增加了一个例外。 -A `samesite=lax` cookie is sent if both of these conditions are true: -1. The HTTP method is "safe" (e.g. GET, but not POST). +`samesite=lax` cookie 在以下两个条件都成立时会被发送: +1. HTTP 方法是安全的(例如 GET 方法,不是 POST)。 - The full list of safe HTTP methods is in the [RFC7231 specification](https://tools.ietf.org/html/rfc7231). Basically, these are the methods that should be used for reading, but not writing the data. They must not perform any data-changing operations. Following a link is always GET, the safe method. + 所有安全的 HTTP 方法列表可以看 [RFC7231 规范](https://tools.ietf.org/html/rfc7231)。基本上,这些都是只能用来读取数据但是不能写入数据的方法。他们不能执行任何更改数据的操作。以下链接都是 GET 安全方法。 -2. The operation performs top-level navigation (changes URL in the browser address bar). +2. 操作执行顶级导航(在浏览器地址栏中改变 URL)。 - That's usually true, but if the navigation is performed in an `