HTTP Cookie

HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会存储 cookie 并在下次向同一服务器再发起请求时携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器——如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。

Cookie 主要用于以下三个方面:

会话状态管理

如用户登录状态、购物车、游戏分数或其他需要记录的信息

个性化设置

如用户自定义设置、主题和其他设置

浏览器行为跟踪

如跟踪分析用户行为等

Cookie 曾一度用于客户端数据的存储,因当时并没有其他合适的存储办法而作为唯一的存储手段,但现在推荐使用现代存储 API。由于服务器指定 Cookie 后,浏览器的每次请求都会携带 Cookie 数据,会带来额外的性能开销(尤其是在移动环境下)。新的浏览器 API 已经允许开发者直接将数据存储到本地,如使用 Web storage APIlocalStoragesessionStorage)或 IndexedDB

备注: 要查看 Cookie 存储(或网页上能够使用其他的存储方式),你可以在开发者工具中启用存储查看器(Storage Inspector)功能,并在存储树上选中 Cookie。

服务器收到 HTTP 请求后,服务器可以在响应标头里面添加一个或多个 Set-Cookie 选项。浏览器收到响应后通常会保存下 Cookie,并将其放在 HTTP Cookie 标头内,向同一服务器发出请求时一起发送。你可以指定一个过期日期或者时间段之后,不能发送 cookie。你也可以对指定的域和路径设置额外的限制,以限制 cookie 发送的位置。关于下面提到的头部属性的详细信息,请参考 Set-Cookie 文章。

服务器使用 Set-Cookie 响应头部向用户代理(一般是浏览器)发送 Cookie 信息。一个简单的 Cookie 可能像这样:

http
Set-Cookie: <cookie-name>=<cookie-value>

这指示服务器发送标头告知客户端存储一对 cookie:

http
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry

[页面内容]

现在,对该服务器发起的每一次新请求,浏览器都会将之前保存的 Cookie 信息通过 Cookie 请求头部再发送给服务器。

http
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry

备注: 如何在以下几种服务端程序中设置 Set-Cookie 响应头信息:

Cookie 的生命周期可以通过两种方式定义:

  • 会话期 Cookie 会在当前的会话结束之后删除。浏览器定义了“当前会话”结束的时间,一些浏览器重启时会使用会话恢复。这可能导致会话 cookie 无限延长。
  • 持久性 Cookie 在过期时间(Expires)指定的日期或有效期(Max-Age)指定的一段时间后被删除。

例如:

http
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

备注: 当 Cookie 的过期时间( Expires)被设定时,设定的日期和时间只与客户端相关,而不是服务端。

如果你的站点对用户进行身份验证,则每当用户进行身份验证时,它都应重新生成并重新发送会话 Cookie,甚至是已经存在的会话 Cookie。此技术有助于防止会话固定攻击(session fixation attacks),在该攻击中第三方可以重用用户的会话。

有两种方法可以确保 Cookie 被安全发送,并且不会被意外的参与者或脚本访问:Secure 属性和 HttpOnly 属性。

标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端。它永远不会使用不安全的 HTTP 发送(本地主机除外),这意味着中间人攻击者无法轻松访问它。不安全的站点(在 URL 中带有 http:)无法使用 Secure 属性设置 cookie。但是,Secure 不会阻止对 cookie 中敏感信息的访问。例如,有权访问客户端硬盘(或,如果未设置 HttpOnly 属性,则为 JavaScript)的人可以读取和修改它。

JavaScript Document.cookie API 无法访问带有 HttpOnly 属性的 cookie;此类 Cookie 仅作用于服务器。例如,持久化服务器端会话的 Cookie 不需要对 JavaScript 可用,而应具有 HttpOnly 属性。此预防措施有助于缓解跨站点脚本(XSS)攻击。

示例:

http
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly

DomainPath 标识定义了 Cookie 的作用域:即允许 Cookie 应该发送给哪些 URL。

Domain 属性

Domain 指定了哪些主机可以接受 Cookie。如果不指定,该属性默认为同一 host 设置 cookie,不包含子域名。如果指定了 Domain,则一般包含子域名。因此,指定 Domain 比省略它的限制要少。但是,当子域需要共享有关用户的信息时,这可能会有所帮助。

例如,如果设置 Domain=mozilla.org,则 Cookie 也包含在子域名中(如 developer.mozilla.org)。

Path 属性

Path 属性指定了一个 URL 路径,该 URL 路径必须存在于请求的 URL 中,以便发送 Cookie 标头。以字符 %x2F (“/”) 作为路径分隔符,并且子路径也会被匹配。

例如,设置 Path=/docs,则以下地址都会匹配:

  • /docs
  • /docs/
  • /docs/Web/
  • /docs/Web/HTTP

但是这些请求路径不会匹配以下地址:

  • /
  • /docsets
  • /fr/docs

SameSite 属性

SameSite 属性允许服务器指定是否/何时通过跨站点请求发送(其中站点由注册的域和方案定义:http 或 https)。这提供了一些针对跨站点请求伪造攻击(CSRF)的保护。它采用三个可能的值:StrictLaxNone

使用 Strict,cookie 仅发送到它来源的站点。Lax 与 Strict 相似,只是在用户导航到 cookie 的源站点时发送 cookie。例如,通过跟踪来自外部站点的链接。None 指定浏览器会在同站请求和跨站请求下继续发送 cookie,但仅在安全的上下文中(即,如果 SameSite=None,且还必须设置 Secure 属性)。如果没有设置 SameSite 属性,则将 cookie 视为 Lax

下面是例子:

http
Set-Cookie: mykey=myvalue; SameSite=Strict

备注:SameSite 相关的标准最近发生了变化(MDN 记录了上面的新行为)。有关在特定浏览器版本中如何处理属性的信息,请参阅 cookie 浏览器兼容性表:

  • 如果 SameSite 未指定,则 SameSite=Lax 时新的默认值。以前,默认情况下会为有请求发送 cookie。
  • SameSite=None 的 cookie 还必须指定 Secure 属性(它们需要安全上下文)。
  • cookie 使用不同的方案(http:https:)发送来自同一域的 cookie,则不再视为来自同一站点。

cookie 的机制使得服务器无法确认 cookie 是在安全来源上设置的,甚至无法确定 cookie 最初是在哪里设置的。

子域上的易受攻击的应用程序可以使用 Domain 属性设置 cookie,从而可以访问所有其他子域上的该 cookie。会话劫持攻击中可能会滥用此机制。有关主要缓解方法,请参阅会话劫持(session fixation)

但是,作为深度防御措施,可以使用 cookie 前缀来断言有关 cookie 的特定事实。有两个前缀可用:

__Host-

如果 cookie 名称具有此前缀,则仅当它也用 Secure 属性标记、从安全来源发送、不包括 Domain 属性,并将 Path 属性设置为 / 时,它才在 Set-Cookie 标头中接受。这样,这些 cookie 可以被视为“domain-locked”。

__Secure-

如果 cookie 名称具有此前缀,则仅当它也用 Secure 属性标记,是从安全来源发送的,它才在 Set-Cookie 标头中接受。该前缀限制要弱于 __Host- 前缀。

带有这些前缀的 Cookie,如果不符合其限制的会被浏览器拒绝。请注意,这确保了如果子域要创建带有前缀的 cookie,那么它将要么局限于该子域,要么被完全忽略。由于应用服务器仅在确定用户是否已通过身份验证或 CSRF 令牌正确时才检查特定的 cookie 名称,因此,这有效地充当了针对会话劫持的防御措施。

备注: 在应用程序服务器上,Web 应用程序必须检查完整的 cookie 名称。用户代理程序在将其发送到请求的 Cookie 标头之前,不会从 cookie 中剥离前缀。

有关 cookie 前缀和浏览器支持的当前状态的更多信息,请参阅Set-Cookie 参考文章的前缀部分

通过 Document.cookie 属性可创建新的 Cookie。如果未设置 HttpOnly 标记,你也可以从 JavaScript 访问现有的 Cookie。

js
document.cookie = "yummy_cookie=choco";
document.cookie = "tasty_cookie=strawberry";
console.log(document.cookie);
// logs "yummy_cookie=choco; tasty_cookie=strawberry"

通过 JavaScript 创建的 Cookie 不能包含 HttpOnly 标志。

请留意在安全章节提到的安全隐患问题,JavaScript 可以通过跨站脚本攻击(XSS)的方式来窃取 Cookie。

安全

备注: 当你存储信息到 Cookie 中时,需要明白 cookie 的值是可以被访问,且可以被终端用户所修改的。根据应用程序的不同,可能需要使用服务器查找的不透明标识符,或者研究诸如 JSON Web Tokens 之类的替代身份验证/机密机制。当机器处于不安全环境时,切记不能通过 HTTP Cookie 存储、传输敏感信息。

缓解涉及 Cookie 的攻击的方法:

  • 使用 HttpOnly 属性可防止通过 JavaScript 访问 cookie 值。
  • 用于敏感信息(例如指示身份验证)的 Cookie 的生存期应较短,并且 SameSite 属性设置为 StrictLax。(请参见上方的 SameSite 属性。)在支持 SameSite 的浏览器中,这样做的作用是确保不与跨站点请求一起发送身份验证 cookie。因此,这种请求实际上不会向应用服务器进行身份验证。

跟踪和隐私

Cookie 与特定域和方案(例如,httphttps)相关联,如果设置了 Set-Cookie Domain 属性,也可能与子域相关联。如果该 cookie 域和方案匹配当前的页面,则认为该 cookie 和该页面来自同一站点,则称为第一方 cookie(first-party cookie)

如果域和方案不同,则它不认为来自同一个站点,被称为第三方 cookie(third-party cookie)。虽然托管网页的服务器设置第一方 Cookie 时,但该页面可能包含存储在其他域中的服务器上的图像或其他组件(例如,广告横幅),这些图像或其他组件可能会设置第三方 Cookie。这些主要用于在网络上进行广告和跟踪。例如,谷歌使用的 cookie 类型

第三方服务器可以基于同一浏览器在访问多个站点时发送给它的 cookie 来建立用户浏览历史和习惯的配置文件。Firefox 默认情况下会阻止已知包含跟踪器的第三方 cookie。第三方 cookie(或仅跟踪 cookie)也可能被其他浏览器设置或扩展程序阻止。阻止 Cookie 会导致某些第三方组件(例如社交媒体窗口小部件)无法正常运行。

备注: 服务器可以(并且应该)设置 cookie SameSite 属性以指定是否可以将 cookie 发送到第三方站点。

涉及使用 Cookie 的法律或法规包括:

  • 欧盟通用数据隐私法规(GDPR)
  • 欧盟的电子隐私权指令
  • 加州消费者隐私法

这些规定具有全球影响力。它们适用于这些司法管辖区的用户访问的万维网上的任何站点(欧盟和加利福尼亚,但请注意,加利福尼亚州的法律仅适用于总收入超过 2500 万美元的实体)。

这些法规包括以下要求:

  • 向用户表明你的站点使用 cookie。
  • 允许用户选择不接收某些或所有 cookie。
  • 允许用户在不接收 Cookie 的情况下使用大部分服务。

可能还存在其他法规来管理你当地的 Cookie。你有责任了解并遵守这些规定。有些公司提供“cookie banner”代码,可帮助你遵守这些法规。

在浏览器中存储信息的其他方式

在浏览器中存储数据的另一种方法是 Web Storage APIwindow.sessionStoragewindow.localStorage 属性与持续时间中的会话和永久 cookie 相对应,但是存储限制比 cookie 大,并且永远不会发送到服务器。可以使用 IndexedDB API 或基于它构建的库来存储更多结构化的数据。

有一些技术旨在在 cookie 被删除后重新创建它们。这些被称为“僵尸”cookie。这些技术违反了用户的隐私和用户控制原则,可能违反数据隐私法规,并可能使使用它们的网站承担法律责任。

参见