浏览器 Web 安全基础系列

  1. 浏览器怎么判断是不是同一个网站

  2. Cookie 为什么会自动携带(当前篇)

  3. Session 登录状态为什么可能被劫持

  4. HTTPS 为什么仍然不够

  5. CORS 到底在限制什么

  6. CSRF 是怎么冒用登录状态的

  7. CSRF 如何防御

  8. XSS 为什么能在你的网站执行

  9. CSP 与浏览器安全响应头

  10. Clickjacking 点击劫持

  11. 第三方资源为什么危险

  12. 防盗链到底防什么

HTTP 本身不记得“上一条请求是谁发的”。用户登录成功后,服务端通常生成 Session ID,通过 Cookie 交给浏览器;浏览器之后访问匹配的地址时自动带回 Cookie,服务端才能识别用户。

Cookie 与 Session 登录流程

一次登录发生了什么

用户提交账号密码:

POST /login
Content-Type: application/json

{"username":"alice","password":"..."}

服务端验证成功后创建 Session:

const sessionId = crypto.randomBytes(32).toString('hex');
sessions.set(sessionId, { userId: 1001 });

再通过响应头写入 Cookie:

Set-Cookie: sid=随机值; Path=/; HttpOnly; Secure; SameSite=Lax

浏览器保存后,访问同一站点时自动发送:

Cookie: sid=随机值

攻击者利用 CSRF 时,依赖的正是“浏览器自动发送”这一行为。

常见方案是只保存随机 Session ID:

Cookie: sid=7f5a...

用户资料、权限和登录状态保存在服务端 Session 或数据库中。这样可以主动注销 Session、更新权限和踢掉设备。

也有无状态 Token 方案,但“把 JWT 放进 Cookie”并不会自动消除 CSRF。只要认证凭证由浏览器自动携带,就仍要考虑跨站请求伪造。

浏览器并不是看到同一域名就发送所有 Cookie,而是依次检查名称、Domain、Path、是否过期、Secure 和 SameSite 等属性。

Cookie 发送判断流程

Domain

不设置 Domain 时通常是 Host-only Cookie,只发给设置它的主机:

Set-Cookie: sid=abc; Path=/

login.example.com 设置后,不会自动发送给 api.example.com

设置:

Domain=example.com

会扩大到子域名。范围越大,受到子域名漏洞和接管问题影响的可能性也越大。

Path

Path=/admin

表示只在匹配路径的请求中发送。Path 主要用于控制发送范围,不是可靠的访问控制边界;其他路径的页面仍可能通过请求访问 /admin

Secure

Secure

表示 Cookie 只通过 HTTPS 发送。生产登录 Cookie 应配合全站 HTTPS 使用。

HttpOnly

HttpOnly

阻止 JavaScript 通过 document.cookie 读取该 Cookie:

console.log(document.cookie); // 看不到 HttpOnly 的 sid

但浏览器发请求时仍会自动携带,所以 HttpOnly 不能防 CSRF,也不能阻止 XSS 脚本借当前登录状态调用接口。它主要降低 Session Cookie 被直接窃取的风险。

SameSite

SameSite=Lax

控制跨站请求是否携带 Cookie:

  • Strict:跨站请求基本不带;防护强,外部链接进入时体验可能受影响。

  • Lax:同站请求携带,跨站顶层安全导航通常可携带,跨站表单 POST 通常不携带。

  • None:允许跨站携带,必须同时使用 Secure

要注意 SameSite 判断的是“站”,不是“源”。localhost:3000localhost:4000 虽然跨源,但仍可能同站。

推荐设置

生产环境可优先考虑:

Set-Cookie: __Host-session=随机值; Path=/; HttpOnly; Secure; SameSite=Lax

__Host- 前缀要求 Cookie 使用 SecurePath=/ 且不能设置 Domain,有助于把 Cookie 限定在当前主机。

Express 示例:

res.cookie('__Host-session', sessionId, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  path: '/',
  maxAge: 30 * 60 * 1000
});

本地纯 HTTP 实验不能完全模拟 Secure Cookie 的生产行为,可以在开发环境条件化关闭,但不要把关闭后的配置复制到线上。

localStorage 不会被浏览器自动放进 HTTP 请求,需要 JavaScript 主动读取并设置请求头;因此传统 CSRF 风险较小。但如果发生 XSS,脚本可以直接读取 localStorage 中的 Token。

Cookie 配合 HttpOnly 可以避免 JavaScript 直接读取 Session ID,但仍要使用 SameSite、CSRF Token 和 Origin 校验处理 CSRF。两种存储方式只是风险侧重点不同。

常见误区

不对。它只限制 JavaScript 读取,浏览器照常发送。

SameSite=Lax 可以替代 CSRF Token

不建议。它不能覆盖同站恶意子域名、兼容性边界和业务特殊流程,应作为多层防御之一。

不对。权限必须由服务端基于用户、角色和资源进行验证。

小结

Cookie 是浏览器保存和自动携带的一小段状态;Session 是服务端对登录状态的管理。Secure 保护传输渠道,HttpOnly 限制 JavaScript 读取,SameSite 限制跨站携带,Domain 和 Path 控制匹配范围。它们各自解决不同问题。

参考资料