Cookies 是那块小饼干?

354 阅读7分钟

我们知道,HTTP 协议是无状态(Stateless)的。服务器和客户端仅在当前请求期间才知道彼此。之后,两个人都忘记了对方。如果服务器仅简单响应静态资源,无状态的影响并不大,并不需要记住对方是谁。但是随着技术的发展,HTTP 的无状态好像并不能满足需求。例如一些网站上的x天免登录,购物车等,如果服务器无法记住客户端的身份,是无法实现的。

那如何能让服务器记住客户端呢?这就要引入 HTTP 中的 Cookies 机制了,简单来说,Cookies 相当于是服务器给每个客户端都贴上的一张小纸条,上面写了一些只有服务器才能理解的数据,需要的时候客户端把这些信息发给服务器,服务器看到 Cookie,就能够认出对方是谁了。

一、什么是 HTTP Cookies

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

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

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

二、Cookies 工作机制

当服务器收到 HTTP 请求时,服务器可以在响应头里面添加一个 Set-Cookie 选项。浏览器收到响应后通常会保存下 Cookie,之后对该服务器每一次请求中都通过  Cookie 请求头部将 Cookie 信息发送给服务器。另外,Cookie 的过期时间、域、路径、有效期、适用站点都可以根据需要来指定。

  • Set-Cookie 响应头字段 服务器使用 Set-Cookie响应头部向用户代理(一般是浏览器)发送 Cookie 信息,通常以 “key=value” 的格式:
Set-Cookie: <cookie >=<cookie >

如果要传递多个 cookie ,那就添加多个 Set-Cookie 头字段,例如:

HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
  • Cookie 请求头字段 浏览器接收到 Cookies 后,会将其存储在浏览器中,之后每次向该服务器发送新的请求时,浏览器都会将之前保存的 Cookie 信息通过 Cookie 请求头部再发送给服务器。
Cookie: <cookie-list>
Cookie: name=value
Cookie: name=value; name2=value2; name3=value3

<cookie-list>:一系列的名称/值对,形式为 <cookie-name>=<cookie-value>。名称/值对之间用分号和空格 ('; ') 隔开。

例如:

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

Cookie工作流程大致如下图所示; 未命名文件 (1).png

三、 Cookie 的属性

3.1 Cookie 的生命周期

(1)会话期 Cookie

会话期 Cookie 在浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效

但是需要注意的是,有些浏览器提供了会话恢复功能,这种情况下即使关闭了浏览器,会话期 Cookie 也会被保留下来,就好像浏览器从来没有关闭一样,这会导致 Cookie 的生命周期无限期延长。

(2)持久性 Cookie

持久性 Cookie 的生命周期取决于过期时间(Expires)或有效期(Max-Age)指定的一段时间。

  • Expires:过期时间,用的是绝对时间点
Set-Cookie: <cookie-name>=<cookie-value>; Expires=<date>

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

  • Max-Age:有效期,单位为秒
Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<non-zero-digit>

3.2 Cookie 的安全性(限制性)

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

(1) Secure 属性

标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端,因此可以预防中间人攻击。

Set-Cookie: <cookie-name>=<cookie-value>; Secure

但即便设置了 Secure 标记,敏感信息也不应该通过 Cookie 传输,因为 Cookie 有其固有的不安全性,Secure 标记也无法提供确实的安全保障,例如,可以访问客户端硬盘的人可以读取它

(2)HttpOnly 属性

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

Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly

3.3 Cookie 的作用域

Domain 和 Path 标识定义了 Cookie 的作用域:允许 Cookie 应该发送给哪些 URL

(1)Domain 属性

Domain 指定了哪些主机可以接受 Cookie。如果不指定,默认为 origin不包含子域名。如果指定了Domain,则一般包含子域名

Set-Cookie: <cookie-name>=<cookie-value>; Domain=<domain-value>

(2)Path 属性

Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F ("/") 作为路径分隔符,子路径也会被匹配。

Set-Cookie: <cookie-name>=<cookie-value>; Path=<path-value>

3.4 SameSite 属性

SameSite Cookie 允许服务器要求某个 cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。

Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Strict
Set-Cookie: <cookie-name>=<cookie-value>; SameSite=Lax
  • None:浏览器会在同站请求、跨站请求下继续发送 cookies,不区分大小写。
  • Strict:浏览器将只在访问相同站点时发送 cookie。
  • Lax:与 Strict 类似,但用户从外部站点导航至 URL 时(例如通过链接)除外。 在新版本浏览器中,为默认选项,Same-site cookies 将会为一些跨站子请求保留,如图片加载或者 frames 的调用,但只有当用户从外部站点导航到 URL 时才会发送。如 link 链接

四、Cookie 的应用

Cookies 的应用很广泛,这里只介绍两种常见的应用。

4.1 保存用户的登录信息

例如将用户id存储于一个cookie内,这样当用户下次访问该页面时就不需要重新登录了,cookie还可以设置过期时间,当超过时间期限后,cookie就会自动消失。也就是我们常见的三天免登录、七天免登录等。

4.2 跟踪用户行为

通常上网的时候会看过很多的广告图片,这些图片背后都是广告商网站(例如 Google),它会“偷偷地”给你贴上 Cookie 小纸条,这样你上其他的网站,别的广告就能用 Cookie 读出你的身份,然后做行为分析,再推给你广告。这种 Cookie 称为第三方 Cookie。

  • 第三方 Cookie Cookie 与域关联。如果此域与您所在页面的域相同,则该 cookie 称为第一方 cookie( first-party cookie) 。即由地址栏中列出的网站域设置的 Cookie。如果域不同,则它是第三方 cookie(third-party cookie)

当托管网页的服务器设置第一方 Cookie 时,该页面可能包含存储在其他域中的服务器上的图像或其他组件(例如,广告横幅),这些图像或其他组件可能会设置第三方 Cookie。这些主要用于在网络上进行广告和跟踪。

第三方服务器可以基于同一浏览器在访问多个站点时发送给它的 cookie 来建立用户浏览历史和习惯的配置文件。

五、Cookie 相关安全问题

5.1 跨站脚本攻击(Cross-site scripting,XSS)

跨站脚本攻击(Cross-site scripting,XSS) 是一种安全漏洞,攻击者可以利用这种漏洞在网站上注入恶意的客户端代码。若受害者运行这些恶意代码,攻击者就可以突破网站的访问限制并冒充受害者

5.2 跨站请求伪造(CSRF)

跨站请求伪造(CSRF)是一种冒充受信任用户,向服务器发送非预期请求的攻击方式

[1] HTTP cookies