前端存储3部曲 - Cookie

540 阅读5分钟

Cookie是什么

简而言之,Cookie就是存储在客户端用于存储会话信息。

主要用于 3 个方面:

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

Cookie由来及使用

HTTP协议是无状态的,每一次请求,客户端(通常浏览器)与服务器之间建立连接,传输数据,数据传输完后,就会关闭连接。这就导致客户端(通常浏览器)发送过来的每一次请求,对于服务器来说都是新的请求。

但在实际产品需求中,例如购物记录,用户中心等就需要在记录登录状态下进行请求。

step1 服务器响应请求,创建Cookie

请求连接成功后,服务器发送到用户浏览器

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

step2 浏览器存储,下一次请求时携带

浏览器存起来并在下次向同一个服务器再发起请求时候携带并发送到服务器

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

浏览器控制台显示位置: 截屏2025-02-08 15.26.06.png

Cookie的生命周期

会话期 Cookie

当Expire属性没有指定值,就是一个会话期 cookie。会在客户端被关闭时被移除。(⚠️一些浏览器重启时会使用会话恢复,这可能导致会话 cookie 无限延长。)

比如下面👇截图中,我手动在浏览器创建的一个cookie,当我把浏览器关闭时,这个cookie就自动移除了。

截屏2025-02-08 15.32.07.png

持久性 Cookie

在过期时间(Expires)指定的日期或有效期(Max-Age)指定的一段时间后被删除。

⚠️过期时间指的是客户端,不是服务端哦~

截屏2025-02-08 15.43.25.png

Cookie常见属性详解

截屏2025-02-08 15.43.25.png 接下来,我们来详拆Cookie属性

Name, Value

Name Cookie名称,Value Cookie值,一般用字母或者数字,不能包含特殊字符(特殊字符需要转码)

Domain

Domain 指定了哪些主机可以接受 Cookie。如果不指定,该属性默认为同一Host设置 cookie,不包含子域名。如果指定了 Domain,则一般包含子域名。!!! 推荐指定Domain,对于一些可能需要在子域名共享的Cookie

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

Path

表示浏览器要发送该 Cookie 标头时,请求的 URL 中所必须存在的路径。

如果path=/ 相当于通配符,什么样的请求URL都可以

如果path有指定,举例 path=/docs, 那么请求路径必须含有 /docs,比如/docs//docs/Web/

Expire、Max-Age

Expire

cookie 的最长有效时间(以客户端时间为准)

Max-Age

在 cookie 过期之前需要经过的秒数。秒数为 0 或负值将会使 cookie 立刻过期。假如同时设置了 Expires 和 Max-Age 属性,那么 Max-Age 的优先级更高。

Size

单个Cookie最大尺寸限制为4096字节

但不建议写尺寸太大的Cookie, 容易请求失败且增加网络传输的开销,导致页面加载速度变慢。在使用移动网络的时候尤其明显。

HttpOnly

JavaScript Document.cookie API 无法访问带有 HttpOnly 属性的 cookie;对于仅作用于服务器的Cookie推荐设置。能够防范XSS攻击

Secure

仅当请求通过 https: 协议(localhost 不受此限制)发送时才会将该 cookie 发送到服务器。有效避免中间人攻击

SameSite

控制 cookie 是否随跨站请求一起发送,这样可以在一定程度上防范跨站请求伪造攻击(CSRF)。

属性值有:

  • strict

浏览器仅对同一站点的请求发送 cookie,即请求来自设置 cookie 的站点。如果请求来自不同的域名或协议(即使是相同域名),则携带有 SameSite=Strict 属性的 cookie 不会被发送。

  • Lax(默认行为)

cookie 不会在跨站请求中被发送,如:加载图像或框架(frame)的请求。但 cookie 在用户从外部站点导航到源站时,cookie 也会被发送(例如,访问一个链接)。

  • None(同时设置Secure, 否则会报错)

浏览器在跨站和同站请求中均会发送 cookie。

截屏2025-02-08 16.58.04.png

Partitioned - 限制跟踪用户行为(必须与Secure一同设置)

比如https://site-a.examplehttps://site-b.example 都用iframe嵌套了https://3rd-party.example

用户先访问了site-a,https://3rd-party.example 在用户设备上设置了 cookie。再访问site-b,嵌入的https://3rd-party.example 仍然可以访问在site-a时存储的Cookie。因为主机键(host key)是同一个。

如果设置了 Partitioned,cookie 将使用两个键进行存储,即主机键和一个新的分区键

访问site-a存储的Cookie的键将变为 {("https://site-a.example"), ("3rd-party.example")}。访问site-b时,分区键不是同一个,就不能够再访问前一页面设置的 cookie。

设置Cookie,获取Cookie函数

除了服务器响应设置cookie

也可通过JavaScript方法 document.cookie设置

document.cookie = "name=oeschger";
document.cookie
//返回的是一串字符串,需要自己解析
//例如'name=oeschger; Pid=1649923743; Sid=3; sugstore=0; ORIGIN=2; bdime=0;'
function setCookie(cname, cvalue, exdays) {  
  const d = new Date();  
  d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));  
  let expires = "expires="+d.toUTCString();  
  document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";  
}  
  
function getCookie(cname) {  
  let name = cname + "=";  
  let ca = document.cookie.split(';');  
  for(let i = 0; i < ca.length; i++) {  
    let c = ca[i];  
    while (c.charAt(0) == ' ') {  
      c = c.substring(1);  
    }  
    if (c.indexOf(name) == 0) {  
      return c.substring(name.length, c.length);  
    }  
  }  
  return "";  
}  

判断客户是否启用 cookie

if (!navigator.cookieEnabled) {
  // 浏览器不支持或阻止设置 cookie。
}

Cookie安全性

Cookie的值可以通过JS来修改,自然能被用来攻击

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

  • 使用 HttpOnly 属性可防止通过 JavaScript 访问 cookie 值。
  • 用于敏感信息的 Cookie 的生存期应较短,并且 SameSite 属性设置为 Strict 或 Lax

如何优化Cookie存储

  1. 开发中减少存储在Cookie的数据量
  2. 细化Domain和path,减少不必要的Cookie传输
  3. 设置合理的过期时间,定期清理

参考资料

MDN - HTTP cookie

阮一峰:Cookie 的 SameSite 属性

W3C- JavaScript Cookies