一文吃透 Cookie

前言

在大多数情况下,设置 Set-Cookie时,大家可能只会设置 keyvalue 属性。当然大部分场景下也只需要做到这样。但是其实 Cookie 在此之外也有很多重要的属性,比如新加入的 Partitioned 属性就相当重要。

Cookie 最初被设计出来是为了解决"状态管理"问题。在 Web 开发中,状态管理是指如何保持用户与网站交互的连续性和一致性,尤其是在 HTTP 协议这种无状态的通信协议中。 每当用户浏览器向服务器发送请求时,服务器都会把它当作一个全新的请求处理,而不知道它是同一个用户的连续请求。这个特性使得实现如用户登录等需要跟踪用户状态的网站功能变得非常困难。 为了解决这个问题,Cookie 被引入作为一种在客户端存储数据的方法,以便跟踪和识别用户。当用户首次访问一个网站时,服务器可以发送一个或多个 Cookie 到用户的浏览器。浏览器随后会存储这些 Cookie,并在以后每次用户再次访问该网站时,自动将这些 Cookie 发送回服务器。通过这种方式,服务器可以识别用户并维护用户的状态信息。

在响应头中返回 Set-Cookienodejs + koa 为例。只需要传入 labelvalue

jsx 复制代码
router.get('/setCookie', (ctx, next) => {
  ctx.cookies.set('testName', 'testValue');
  ctx.body = 'success';
});

设置成功后,在浏览器中就可以看到了

为了满足我们各种各样的需求,我们在 Set Cookie 时,也提供了一系列的参数。 具体可以查看 MDN 文档:developer.mozilla.org/zh-CN/docs/...

Domain

配置 Cookie 的所属域名。一般会设置当前域名,或者子域名。默认是设置为当前域名。

jsx 复制代码
router.get('/setCookie', (ctx, next) => {
  // 将 cookie 设置到当前域名下
  ctx.cookies.set('testName', 'testValue');
  // 将 cookie 设置到顶级域名下
  ctx.cookies.set('testName2', 'testValue2', {
    domain: "testorigin.com",
  });
  ctx.body = 'success';
});

可以看到 Set-Cookie 中增加了 Domain 配置 Chrome 中可以看到,Domain 列。二者也是不一样的。 它们的区别在于 testName 只有在访问 testsuborigin.testorigin.com 域名下的资源才会自动带上 testName2 在访问 *.testorigin.com 域名下的资源都会带上

Expires

设置 Cookie 的过期时间,默认的有效期为当通会话。如果设置了 Expires 日期,其截止时间与客户端相关,而非服务器的时间。(我用 Chrome 试了一下,只有关闭浏览器他才过期。猜测可能有个失效,在时效内关闭 tab 。也不会清除。)

jsx 复制代码
router.get('/setCookie', (ctx, next) => {
  ctx.cookies.set('testName', 'testValue');

  ctx.cookies.set('testName3', 'testValue3', {
    expires: new Date('2024-03-22 16:57'),
  });

  ctx.body = 'success';
});

它在浏览器里长这样

HttpOnly

控制是否可以通过 Document.cookie 属性访问 Cookie。默认为 true。 可以看到这里只可以获取到 testName4

Max-Age

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

Path

表示浏览器要发送该 Cookie 标头时,请求的 URL 中所必须存在的路径。 正斜杠(/)字符可以解释为目录分隔符,且子目录也满足匹配的条件。例如,如果 path=/docs,那么

  • 请求路径 /docs/docs//docs/Web//docs/Web/HTTP 都满足匹配条件。
  • 请求路径 //docsets 或者 /fr/docs 则不满足匹配条件。

SameSite

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

  • Strict:关于这个属性的解释,我看了 MDN 上的文档,写的不太对。根据我的测试,应该这样解释:如果在 a.com 下有一个 Strict 属性的 Cookie。则当任何非同源的地址,跳转到 a.com 时。都无法携带该 cookie。如果你直接在浏览器中输入 a.com 或者从同源地址跳转到 a.com 是可以正常携带的。并且跨域请求的话也是无法携带该 Cookie

比如,当前网页(非 *.github.com)有一个 GitHub 链接,用户点击跳转就不会带有 GitHub 的 Cookie,跳转过去总是未登陆状态。(via 阮一峰)

  • Lax:默认值,和 Strict 的唯一区别是没有了跳转时限制。
  • None:当跨域请求时,依然可以携带该 Cookie。这个需要谨慎设置,可能会遭到 CSRF 攻击。

Partitioned

这是浏览器新出的一个配置项,需要 Chrome 114 。要解释这个属性的作用,还得从Third-party Cookie 说起。

什么是 Third-party cookie: 用户访问 jd.com 时,会在 jd.com 下种下 cookie。然后,用户又去访问 douyin.com 。这时候,如果 douyin.com 中有访问 jd.com 的资源,就会带上 jd.com 之前种下的 cookie。这也解释了,为啥你在 jd 浏览了什么商品,在 douyin 就能刷到这个商品的广告。😜

不知道小伙伴有没有看到过下面这个警告,是的浏览器要开始禁用 Third-party Cookie 了。 当然,你可以可以通过浏览器的设置,来禁用 Third-party Cookie 这么做的目的,是为了保护用户隐私,即为了阻止 Cross-site tracking(跨站点跟踪)。

什么是跨站点跟踪: 用户访问 site-a.example ,其中嵌入了来自 3rd-party.example 的内容。 3rd-party.example 在用户的设备上设置cookie。 用户访问 site-b.example ,它也嵌入了 3rd-party.example 。这个新的 3rd-party.example 实例仍然能够访问用户在上一页时设置的cookie。(via MDN)

那么是不是直接禁用 Third-party cookie 就可以了呢,当然也不能这么绝对,因为还有一些合理的 Third-party cookie 。他们不是用来跟踪用户的,例如,跨不同站点的嵌入式地图或聊天小部件的持久化状态,以及为子资源CDN负载平衡和Headless CMS提供程序持久化配置信息。(via MDN) 那么 Partitioned 是如何做到的呢?

举个例子: 用户访问 shoppy.example ,其中嵌入了来自 3rd-party.example/chat 的第三方聊天服务,为需要帮助的用户提供支持。 3rd-party.example/chat 使用 Partitioned 在用户的设备上设置cookie,以在不同的站点子域中保持聊天状态。 Cookie的存储密钥为 {("shoppy.example"), ("3rd-party.example/chat")} 。 用户访问各种子域以解决他们的问题,这些子域也嵌入了 3rd-party.example/chat ,包括 support.shoppy.examplecheckout.shoppy.example 。新嵌入的实例能够访问cookie,因为分区键仍然匹配。(via MDN)

总结

本文介绍了 Cookie 的用途, 以及 DomainExpiresHttpOnly 等选项的作用。探讨了 SameSite 属性对 CSRF 防御的影响,以及新的 Partitioned 属性如何帮助保护用户隐私,限制 Third-party Cookie 的跨站跟踪功能。

相关推荐
余衫马2 分钟前
Rust-Trait 特征编程
开发语言·后端·rust
JerryXZR4 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
monkey_meng5 分钟前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
七星静香6 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596939 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai15 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_91523 分钟前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼1 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
paopaokaka_luck5 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
逐·風5 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#