Cookie

介绍

我们常说「HTTP 是无状态的」,这里的 HTTP 指 HTTP 1.x。无状态指的是同一个客户端连续发送多次请求给服务端,服务端无法获知是否来自同一客户端。为了解决 HTTP 无状态导致的这个问题,Lou Montulli 提出通过 Cookie 的概念,他当时的目的是为了解决购物车问题。他最早的文档 提供了一些 Cookie 工作原理等基本信息,这些内容在 RFC 2109 中被规范化,并最终形成 RFC 2965

HTTP 无状态并非不好,它也有优势:服务端可以根据需要将请求分发到集群的任意节点,有利于缓存和负载均衡。另外,HTTP 无状态是指协议层的无状态,Cookie 并非为了通讯协议的无状态问题,它解决的是客户端与服务端通讯会话状态的问题。

设置

有两种方式设置 Cookie,一种是服务端接收到请求后,在响应头中添加 Set-Cookie 字段,客户端接收到响应后会进行保存;另一种方式是客户端通过 docuemnt.cookie 进行设置。

属性

  • Name

    Cookie 的存储方式类似于 key-value,Name 可以理解为 key;尽量不要使用中文存储

  • Value

    Cookie 的值,存储前后进行编码和接码操作

  • Expires

    设置 Cookie 的过期时间,必须是 GMT 格式

    1
    2
    3
    new Date().toGMTString()

    new Date().toUTCString()

    如果不设置该属性,则默认为 session ,即会话 Cookie

  • Max-age

    Max-age 属性也是控制 Cookie 的有效期,单位为秒;取值可以为负数、0 或者正数;负数或者 0 时表示删除 Cookie。Max-age 权重高于 Expires

  • Domain、Path

    DomainPath 一起限制 Cookie 允许被哪些 URL 访问,并决定哪些 Cookie 可以被添加到请求头中。

    如果不设置,则会使用默认值;Domian 默认值为 location.hostnamePath 默认值为 location.pathname

    另外,不能设置跨域 Cookie。

  • HttpOnly

    HttpOnly 表示是否允许通过客户端脚本访问(包括读取、编辑、删除);客户端无法将该属性设置为 true,只能通过服务端设置。

  • Secure

    Secure 用于设置当前 Cookie 是否仅适用于 HTTPS;如果设置了该属性,则仅在 HTTPS 的情况下才会在请求头中添加当前 Cookie。

  • SameSite

    SameSite 属性表示在跨站请求中是否携带 Cookie,包含以下三个值,其中默认值为 None

    • Strict:只有同站请求才允许携带 Cookie

    • Lax:同站请求或在“顶级导航”(指非 iframe 的页面)中使用 安全的 HTTP 方法 请求才能携带 Cookie

      If the value is “Lax”, the cookie will be sent with same-site requests, and with “cross-site” top-level navigations, as described in Section 5.3.7.1.

      In the interests of providing a drop-in mechanism that mitigates the risk of CSRF attacks, developers may set the “SameSite” attribute in a “Lax” enforcement mode that carves out an exception which sends same-site cookies along with cross-site requests if and only if they are top-level navigations which use a “safe” (in the [RFC7231] sense) HTTP method.

      “Safe” HTTP methods include “GET”, “HEAD”, “OPTIONS”, and “TRACE”, as defined in Section 4.2.1 of [RFC7231].

    • None:无论请求是否跨站,都会携带 Cookie

    跨站与跨域不同,跨域是受同源策略(SOP)的限制,需要同时判断 $协议域名端口号协议 + 域名 +端口号$。Cookie 中的跨站的判断相对简单些,只需要判断 $二级域名有效顶级域名二级域名 + 有效顶级域名$ 是否一致。如:www.baidu.comwww.google.com 之间属于跨站,但 www.baidu.comwww.pan.baidu.com 之间属于同站。

    关于 SameSite 有几点需要留意的:

    • Chrome 80 开始将默认值设为 Lax

    • 通过服务端设置 SameSite 时需同时设置 Secure=true

    • 有部分客户端浏览器无法识别 SameSite=none 或把 SameSite=none 识别成 SameSite=Strict,因此在服务端在设置 Cookie 时需要设置两次

      1
      2
      Set-cookie: key=value; SameSite=None; Secure
      Set-cookie: key=value; Secure

分类

  • 会话 Cookie

    会话 Cookie 不指定 Expires 或者 Max-Age 属性,在 Chrome 中会话 Cookie 的 Expires / Max-Age 值为 Session。会话 Cookie 存放于浏览器内存中,浏览器关闭后,会话 Cookie 会被自动删除。

  • 持久性 Cookie

    持久性 Cookie 可以指定过 Expires 或者 Max-Age

    持久性缓存存储于计算机硬盘中,在 Mac 中,Chrome 的持久性 Cookie 存放在 ~/Library/Application Support/Google/Chrome/Default/Cookie 文件中。

其他

自动删除

Cookie 在以下场景下会被自动删除

  • 会话 Cookie 在浏览器关闭后会被自动删除
  • 持久性 Cookie 在到达失效时间时会被自动删除
  • 浏览器中 Cookie 数量达到限制,Cookie 会被删除,为新的 Cookie 腾出空间

限制

RFC 2109 定义 Cookie 的最小化实现标准,但不同的浏览器的实现不一样;在 Cookie 大小、数量上的限制也不一样。

BrowserMax CookieMax Size Per CookieMax Size Per Domain
Chrome1804096NA
FirefoxNA4097NA
Firefox NightlyNA4097NA
SafariNA4097NA
IE 6 / IE 72040964096
IE 8+50409610234

具体浏览器限制,可以在 Browser Cookie Limits 进行测试。

发展

Cookie 起初主要用于存储客户端数据,当时没有其他合适的存储方式,只能使用 Cookie 进行存储。但随着浏览器的发展,现代浏览器开始支持各种存储方式,如:Web Storage、IndexedDB,Cookie 的作用逐渐减小。另外每次请求都会携带相应的 Cookie,会带来额外的性能开销,因此在存储客户端数据方面,可以更倾向于采用新的存储方式。

参考