findxc / blog

88 stars 5 forks source link

cookie cookie cookie ! #51

Open findxc opened 3 years ago

findxc commented 3 years ago

参考资料

本地测试环境搭建

弄了一个有前端代码和后端代码的仓库来在本地进行各种测试,见 https://github.com/findxc/frontend-and-backend-playground

通过 npm run start-test-cookie 来启动前后端服务,需要先本地生成 SSL 证书哦,详见本文底部。

cookie 的使用场景

参见 Using HTTP cookies - HTTP | MDN

Cookies are mainly used for three purposes:

Session management Logins, shopping carts, game scores, or anything else the server should remember

Personalization User preferences, themes, and other settings

Tracking Recording and analyzing user behavior

以登录来举例,前端在登录页面发起一个登录请求,然后服务端在 response headers 中通过 set-cookie 来设置 cookie 的值,这之后前端在发起请求时浏览器会自动在 request headers 中携带上 cookie ,服务端校验 cookie 是有效的则返回相应数据给前端。

关于通过 cookie 来记录和分析用户行为参见 你是如何被广告跟踪的? - 知乎

set-cookie 的参数解释

在浏览器控制台中可以看到当前这个域下的所有 cookie ,如果有多个就有多行,每个 cookie 都有一些属性值。允许不同 cookie 设置不同属性值。

A8222E39-8AC8-45AA-ADA8-56C2D59CEAA8

Name 和 Value 就是某个 cookie 的名字和值。

Domain 指这个 cookie 是和哪个 Domain 相关的,比如当请求 some.com 的资源时:

Path 指请求的 url 中需要包含的路径。

Expires / Max-Age 指 cookie 过期时间,如果不设的话默认值是 session ,当完全退出浏览器时(关闭掉这个应用进程,不仅仅是关闭浏览器窗口)会清除掉 cookie 。(如果说浏览器有自动恢复关闭窗口的功能,也可能不会清除掉 cookie ,所以一般还是手动设置一个过期时间比较好)

HttpOnly 如果设为 true ,那么前端不能通过 document.cookie 来访问该 cookie 值,如果前端没有访问 cookie 的必要,建议都加上该属性,避免 cookie 泄漏。

Secure 如果设为 true ,那么一定要通过 HTTPS 来请求资源才会携带 cookie / 才能 set-cookie 成功(localhost 例外)。

SameSite 如果不设,默认值为 Lax ,也可以设为 Strict 和 None 。

比如 GitHub 的 cookie 绝大部分是 Lax ,当你已经登录了 GitHub 后,然后你从其它网站通过链接又打开 GitHub 时,由于允许携带 cookie ,所以你现在依然是登录状态。而如果说 cookie 是 Strict ,那么由于请求 github.com 时没携带 cookie 那么页面会是未登录的。

8EF82430-98FC-4599-AE7F-0EAC8C1E36CC

再举个例子,YouTube 的 cookie 是 None ,为啥呢?

因为 YouTube 有个功能是允许视频嵌入在其它网站中,用 iframe 的形式来播放,那么为了当你已经登录过 YouTube 后,在其它网站看到 YouTube 视频时也能一键添加到「稍后再看」,就需要 cookie 是允许跨站的,比如说 Introducing Zero-Bundle-Size React Server Components – React Blog 里面的 YouTube 视频。

06952B30-3EA1-4029-9045-7968C103822F

B0EBC6D3-8D76-4401-9016-93FF6A19E840

SameParty 和 Priority 是 Chrome 的属性,暂未普及,不细说了。

关于 Cookie prefixes

Using HTTP cookies - HTTP | MDN 有介绍。

在上面 GitHub 和 YouTube 的 cookie 里可以看到有名字以 __Secure-__Host- 开头的,它们是有特殊含义的。

__Secure- 开头的 cookie 必须同时设置 Secure 为 true ,也就是这类 cookie 肯定是通过 HTTPS 设置上的。

__Host- 开头的 cookie 必须同时设置 Secure 为 true 并且不设置 Domain ,也就是这类 cookie 不会说是子域名设置上的,只可能是当前服务端域设置上的。

如果说你恰好有某些 cookie 是希望确定就是自己这个域名设上的,那就可以试试 __Host- 。(但是看 GitHub 和 YouTube 好像没咋用这个属性 🤔 )

本地测试一下

首先得弄几个不同的域名,mac 下在终端中执行 sudo vi /etc/hosts ,然后在文件末尾补充:

127.0.0.1 aaa.com
127.0.0.1 bbb.com
127.0.0.1 some.com
127.0.0.1 a.some.com
127.0.0.1 b.some.com

这样当访问 aaa.com 其实就是访问 127.0.0.1 ,也就是 localhost 了。

然后再弄个 SSL 证书,这样本地启动的服务也可以使用 HTTPS 访问了。

使用 mkcert 来生成证书,安装好后,执行 mkcert -key-file ssl-key.pem -cert-file ssl-cert.pem localhost aaa.com bbb.com some.com a.some.com b.some.com 来生成证书,在本地启动前端/后端服务时配置上证书就能用 HTTPS 来访问了。

比如用 serve 来启动前端, 那么命令就类似于这样: serve —ssl-cert ./ssl-cert.pem —ssl-key ssl-key.pem your_dir

比如用 express 写的后端,那么代码就类似于下面:

// 支持 http 访问的服务
http.createServer(app).listen(PORT)

const keyPath = path.resolve(__dirname, '../ssl-key.pem')
const certPath = path.resolve(__dirname, '../ssl-cert.pem')
// 如果有 ssl 证书,那么再启动一个支持 https 访问的服务
if (fs.existsSync(keyPath)) {
  const options = {
    key: fs.readFileSync(keyPath),
    cert: fs.readFileSync(certPath),
  }
  https.createServer(options, app).listen(PORT + 1)
}

ok,然后就可以开始愉快地各种测试了,把 cookie 的各个属性都测试一遍就懂啦。