# HTTP Cookie

HTTP Cookie 是 server 发送到用户浏览器的一小段数据。浏览器会存储 Cookie 并在以后的请求中将其发送回同一 server。通常，HTTP Cookie 用于判断两个请求是否来自同一浏览器（比如保持用户登录），将状态引入到无状态的 HTTP 事务中。

Cookie 通常是由 web 服务器创建，并由浏览器放置到用户计算机上的一小块数据。它让 web server 能够在用户设备上存储状态信息。

Cookie 就是服务器委托浏览器存储在客户端里的一些数据，而这些数据通常都会记录用户的关键识别信息，从而让服务器有了记忆能力。

![](https://605825044-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVUa7MztTVfoHQxXozBQf%2Fuploads%2F0gdRuE8sfPNEFWQrbyJ6%2Fimage.png?alt=media\&token=eccb24a2-bf7d-4e67-8027-7f2ce92877f3)

## 工作原理

有了 cookie 之后，流程大约是：

1. 当用户通过浏览器第一次访问服务器时，服务器肯定不知道它的身份。所以，就要创建一个独特的身份标识数据，放进 `Set-Cookie` header 里，然后随着响应报文一同发给浏览器
2. 浏览器收到响应报文之后，看到里面有 `Set-Cookie` header，知道这是服务器给的身份标识，于是就保存起来，等下次再请求的时候就自动把这个值放进 `Cookie` header 里发给服务器
3. 因为第二次请求里有了 `Cookie` header 字段，所以服务器就知道了这个用户不是新人，之前来过，便可以识别出用户的身份，然后提供个性化的服务

<table><thead><tr><th width="138.33333333333331">Request</th><th width="207">Response</th><th>说明</th></tr></thead><tbody><tr><td></td><td><mark style="color:purple;">Set-Cookie</mark>: key=value</td><td>可重复多个</td></tr><tr><td><mark style="color:purple;">Cookie</mark></td><td></td><td>多个值之间用 <code>;</code> 隔开</td></tr></tbody></table>

> 说明：由于 cookie 并不属于 HTTP 标准（RFC6265，而不是 RFC2616/7230），所以语法上和其它标头字段不一致，分隔符用的是 `;` 而不是 `,`

```http
# response
Set-Cookie: k1=v1
Set-Cookie: k2=v2
```

```http
# request
Cookie: k1=v1; k2=v2
```

然后，对于向 server 发出的每个后续 request，浏览器都会用 Cookie header 将所有先前存储的 cookie 发送回 server。仅发送 cookie 的名称和值，并不会带 cookie 的属性。

## Cookie 的属性

为了防止 cookie 被外泄和窃取，服务器可以设置以下属性。

### 生命周期

cookie 的生命周期，有两种定义方式：

1. Session Cookie：当 current session 结束时，会被删除
2. Permanent Cookie：过期时间由 `Max-Age` 或 `Expires` 属性来指定
   * `Expires`：绝对时间点，过期时间/截止日期，表示浏览器应删除 cookie 的特定日期和时间
   * <mark style="color:purple;">`Max-Age`</mark>：相对时间，单位是秒，是相对于浏览器收到报文的时间点
   * 当两者同时存在时，`Max-Age` 的优先级更高

```http
Set-Cookie: k3=v3; Max-Age=3
Set-Cookie: k4=v4; Expires=xxx; Secure; HttpOnly
```

### 作用域

cookie 的作用域：cookie 应发送到哪些 URL。

1. `Domain`：指定哪些 host 可以接收 cookie
   * 如果没有指定该属性，浏览器会默认设置为所请求资源的相同 host，但不包括子域
   * 如果指定了该属性，则始终包含子域。比如 `Domain=mozilla.org`，则该 cookie 在子域上也能使用，比如 `developer.mozilla.org`
2. `Path`：指定请求的 URL 中必须包含的 path。比如 `Path=/docs`
   * 则以下 request path 匹配：`/docs`, `/docs/`, `/docs/Web/`, `/docs/Web/HTTP`
   * 则以下 request path 不匹配：`/`, `/docsets`, `/fr/docs`

### 安全性

```http
Set-Cookie: k1=v1; Expires=xxx; Secure; HttpOnly
```

1. `Secure`：表示此 cookie 只能用于 HTTPS 协议，HTTP 会被禁止发送。但 cookie 本身不是加密的，浏览器里还是以明文的形式存在
2. <mark style="color:purple;">`HttpOnly`</mark>：表示此 cookie 只能通过 HTTP 协议传输，禁止其它访问方式。比如客户端就没法使用 JavaScript 的 `document.cookie` 等一切相关的 API 访问了，从而避免了 XSS 窃取 cookie
3. `SameSite`：表示能否通过跨站点请求发送 cookie，可以防范 CSRF 攻击（跨站请求伪造）
   * `SameSite=Strict` 严格限定 cookie 不能随跳转链接跨站发送，浏览器只会将 cookie 发送到与源域相同的目标域
   * `SameSite=Lax` 略微宽松，允许 GET/HEAD 等安全方法，但禁止 POST 跨站发送
   * `SameSite=None`

## Cookie 的应用

有了 cookie，服务器就有了记忆能力，能够保存状态。

1. 身份识别。保存用户的登录信息，实现有状态的会话事务
2. 个性化：用户偏好、主题和其它设置
3. 追踪：记录并分析用户行为
4. 广告跟踪。这种 cookie 不是由访问的主站存储的，所以又叫 third-party cookie。为了防止滥用 cookie 搜集用户隐私，互联网组织相继提出了 DNT（Do Not Track）和 P3P（Platform for Privacy Preferences Project），但实际作用不大。

### 替代方案

一些可以使用 cookie 完成的操作，也可以用其它机制：

1. 身份验证和会话管理
   1. HTTP 认证：基本访问认证、摘要访问认证协议
   2. JSON Web Token（JWT，JSON 网络令牌）：一个独立的信息包
   3. URL 的查询字符串：由 server 将包含唯一会话标识符的查询字符串附加到网页内的所有链接组成
   4. 带有隐藏字段的 Web 表单：
   5. window\.name DOM 属性
2. 追踪
   1. 浏览器指纹
   2. HTTP ETag
   3. IP地址：并不可靠，因为有公共 IP
   4. 浏览器缓存
3. 网络存储：Web Storage API、 IndexedDB
   * 值得一提的是，cookie 作为客户端存储的方式正在被淘汰中。客户端存储，更建议用现代存储 API：Web Storage API（localStorage + sessionStorage）和 IndexedDB
   * `window.sessionStorage` 和 `window.localStorage` 属性在持续时间上分别对应：会话 cookie 和永久 cookie，但比 cookie 有更大的存储限制，并且永远不会发送到服务器
   * 而 IndexedDB API 可以存储更结构化、更大量的数据

<https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies>
