HTTP Cookie 综合介绍:机制、属性与安全

152 阅读9分钟

这是一篇关于 HTTP Cookie 的综合性介绍,整合了其机制、属性、安全考量以及与 localStorage 的对比。


HTTP Cookie 综合介绍:机制、属性与安全

一、 什么是 Cookie?为什么需要它?

HTTP 协议本身是无状态 (Stateless) 的。这意味着服务器默认不会“记住”来自同一浏览器的连续请求。为了解决这个问题,让网站能够识别用户、跟踪会话状态(比如用户是否登录、购物车里有什么)、存储用户偏好等,Cookie 应运而生。

Cookie 本质上是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会在后续向该服务器发起请求时,自动将这块数据携带回去,从而让服务器能够识别出是同一个用户。

二、 Cookie 的工作流程(生命周期)

  1. 服务器设置 (Server Sets): 当浏览器向服务器发起请求(例如登录),服务器如果需要在浏览器端存储信息,就会在 HTTP 响应头 (Response Headers) 中包含一个或多个 Set-Cookie 字段。每个 Set-Cookie 字段定义一个 Cookie 的名称、值和相关属性。

    HTTP/1.1 200 OK
    Content-Type: application/json
    Set-Cookie: session_id=abc123xyz; Path=/; Expires=Sat, 02 May 2026 23:38:25 GMT; Secure; HttpOnly; SameSite=Lax
    Set-Cookie: theme=dark; Path=/; Max-Age=31536000
    
    • 关键点: 设置指令 (Set-Cookie) 出现在响应头中。服务器可以一次设置多个 Cookie,每个对应一个 Set-Cookie 头。
  2. 浏览器存储 (Browser Stores): 浏览器收到带有 Set-Cookie 的响应后,会解析这些指令,并将 Cookie 数据根据其指定的域名 (Domain) 和路径 (Path) 存储在本地。

  3. 浏览器自动发送 (Browser Automatically Sends): 当浏览器下一次同一个服务器(且请求 URL 匹配 Cookie 的 DomainPath 属性)发起 HTTP 请求(无论是页面、图片、API 调用等)时,浏览器会自动查找所有相关的、未过期的 Cookie,并将它们的 name=value 对组合起来,放入 HTTP 请求头 (Request Headers)Cookie 字段中发送给服务器。

    GET /api/user/profile HTTP/1.1
    Host: yourdomain.com
    Cookie: session_id=abc123xyz; theme=dark
    Accept: application/json
    ...
    
    • 关键点: Cookie 数据 (Cookie) 出现在请求头中。这个自动发送机制是 Cookie 的核心特性,也是 CSRF 攻击得以发生的基础。浏览器会根据 SecureSameSite 属性决定在特定情况下是否发送 Cookie。
  4. 服务器读取 (Server Reads): 服务器收到请求后,解析 Cookie 请求头,获取之前存储的数据,从而识别用户或恢复状态。

三、 Cookie 的重要属性详解

每个 Set-Cookie 指令除了基本的 name=value 对,还可以包含多个属性来控制 Cookie 的行为:

  • Expires=<date>Max-Age=<seconds>: 控制 Cookie 的生命周期
    • Expires: 指定一个具体的过期日期和时间 (GMT/UTC)
    • Max-Age: 指定 Cookie 从创建开始可以存活的秒数Max-Age 优先级高于 Expires
    • 如果两者都未设置,则 Cookie 是会话级 (Session Cookie),浏览器关闭后通常会被删除。
  • Domain=<domain-value>: 指定 Cookie 所属的域名范围。默认是当前文档的域名(不含子域)。可以设置为父域名(如 .example.com)让子域名共享。
  • Path=<path-value>: 指定 Cookie 生效的URL 路径范围/ 表示整个域名下都有效。
  • Secure: (安全属性) 一个布尔标记。如果设置了 Secure,浏览器在通过 HTTPS 协议请求时才会发送该 Cookie。这可以防止 Cookie 在不安全的 HTTP 连接中被中间人窃听。强烈建议所有敏感 Cookie 都设置此属性。
  • HttpOnly: (安全属性) 一个布尔标记。如果设置了 HttpOnly,该 Cookie 将无法通过客户端 JavaScript (document.cookie, fetch API 等) 访问。这是防御 XSS (跨站脚本攻击) 窃取会话 Cookie 的核心手段。即使网站存在 XSS 漏洞,攻击者的脚本也无法直接读取到 HttpOnly 的 Cookie 值。强烈建议用于存储会话标识等敏感信息的 Cookie 设置此属性。
  • SameSite=<Strict | Lax | None>: (安全属性) 控制浏览器在跨站请求时是否发送 Cookie,是防御 CSRF (跨站请求伪造) 攻击的关键机制
    • Strict: 最严格。任何跨站请求都不会发送 Cookie。提供最强的 CSRF 防护,但可能影响从外部链接点击进入网站时的用户体验(可能导致“被登出”)。适用于高度敏感操作的 Cookie。
      • 具体场景:

        • 会发送 Cookie 的情况:  用户在 yoursite.com 页面上点击一个指向 yoursite.com/profile 的链接或提交一个指向 yoursite.com/update 的表单。

        • 不会发送 Cookie 的情况:

          • 用户在 othersite.com 上点击一个指向 yoursite.com 的链接。

          • 用户在 othersite.com 上提交一个指向 yoursite.com 的表单(这是典型的 CSRF 攻击场景)。

          • othersite.com 页面中嵌入的资源(如 <img><iframe>)向 yoursite.com 发起请求。

    • Lax (宽松 - 现代浏览器默认值): 平衡安全与体验。跨站的顶层导航 (Top-level navigation) 且使用安全 HTTP 方法 (主要是 GET,如点击链接跳转) 时才发送 Cookie。阻止跨站 POST 表单、<img><iframe>、AJAX 等高风险 CSRF 场景发送 Cookie。适用于大多数会话 Cookie。
      • 具体场景:

        • 会发送 Cookie 的情况:

          • 用户在 yoursite.com 内部导航或提交表单。
          • 用户在 othersite.com 上点击一个链接 (<a href="...">) 指向 yoursite.com (因为这是顶层导航且通常是 GET 请求)。
        • 不会发送 Cookie 的情况:

          • 用户在 othersite.com 上提交一个表单 (<form method="POST">) 指向 yoursite.com (因为 POST 不是安全方法)。

          • othersite.com 页面通过 <img><iframe>, AJAX (fetch, XMLHttpRequest) 等方式向 yoursite.com 发起跨站资源请求或后台请求 (因为这些不是顶层导航)。

    • None: 允许在任何跨站请求(包括 AJAX、iframe 等)中发送 Cookie。但必须同时设置 Secure 属性 (即仅限 HTTPS)。适用于需要跨域嵌入或 API 调用的场景,但需要开发者自行实现其他 CSRF 防护措施。
  • 关键点解释:

    • 顶层导航 (Top-level navigation):  指的是导致浏览器地址栏 URL 变化的导航操作,最典型的就是用户直接点击一个 <a href="..."> 链接。

    • 安全的 HTTP 方法 (Safe HTTP methods):  通常指 GET, HEAD, OPTIONS, TRACE。这些方法理论上不应该改变服务器状态(主要是 GET)。

    • 核心概念:什么是“站点”(Site)?

      在 SameSite 的语境下,“站点”通常指你的顶级域名加上其前缀(例如 example.com)。子域名(如 login.example.com 和 shop.example.com)被认为是同一站点 (Same Site) 。而完全不同的域名(如 example.com 和 another-site.org)则被认为是跨站 (Cross Site)

      SameSite 属性就是用来控制,当用户从一个不同的站点(Cross Site)向你的站点发起请求时,浏览器是否应该自动携带你站点的 Cookie。

四、 安全考量:XSS 与 CSRF

  • XSS (Cross-Site Scripting): 如果网站存在 XSS 漏洞,攻击者可以注入恶意脚本。如果 Cookie 没有设置 HttpOnly,恶意脚本可以通过 document.cookie 读取用户的敏感 Cookie(如会话 ID),然后发送到攻击者的服务器,导致会话劫持。HttpOnly 是防御此类攻击的关键。
  • CSRF (Cross-Site Request Forgery): 攻击者在自己的恶意网站上设置一个指向你网站的请求(如一个自动提交的表单),诱导已登录你网站的用户访问该恶意网站。由于 Cookie 的自动发送机制,用户的浏览器会自动携带你网站的 Cookie 发起这个恶意请求,从而以用户的身份执行非预期的操作(如转账、修改设置)。SameSite 属性 (特别是 LaxStrict) 是防御此类攻击的主要手段。

五、 Cookie vs. localStorage 对比

特性Cookie (HttpOnly, Secure, SameSite=Lax)localStorage
主要用途会话管理、身份验证凭证客户端数据存储(设置、离线数据等)
存储大小小 (~4KB)大 (~5-10MB)
生命周期可设置过期,可会话级永久,需手动/代码清除
与服务器通信自动发送 (每次请求)需手动 (JS 读取后添加到请求头)
JS 访问不可访问 (HttpOnly)可访问
XSS 防护高 (HttpOnly) (易被 XSS 窃取)
CSRF 防护高 (SameSite=Lax/Strict)较高 (不自动发送)
API 易用性读写稍繁琐 (服务器端处理)简单 (setItem, getItem)

六、 如何选择?

  • 场景一:身份验证令牌(如 Session ID 或 Opaque Token),主要由服务器验证,客户端 JS 不需要直接读取。

    • 最佳选择:Cookie + HttpOnly + secure + samesite=Lax 或 Strict。  这是最安全的标准实践。服务器在登录成功后通过 Set-Cookie 响应头设置,浏览器自动管理和发送,JS 无法读取,有效防御 XSS 和 CSRF。
  • 场景二:API 令牌(如 JWT),客户端 JS 需要读取并添加到 Authorization: Bearer  请求头中。

    • 选择 A (常用但安全性较低): localStorage。  实现简单,但必须意识到 XSS 风险,需要采取其他措施加固应用安全(如严格的内容安全策略 CSP、输入验证、输出编码等)。
    • 选择 B (安全性与 A 类似): Cookie (非 HttpOnly)。  JS 可以读取,但仍易受 XSS 攻击。相比 localStorage 没有明显安全优势,反而可能因自动发送带来 CSRF 风险(需依赖 samesite)。
    • 选择 C (更安全但可能更复杂):  结合使用。例如,使用安全的 HttpOnly Cookie 维护用户会话状态,当需要调用 API 时,由后端(或 BFF - Backend for Frontend)根据会话 Cookie 生成临时的、短效的 API Token 返回给前端,前端将其用于 API 请求头,但不长期存储。或者前端通过一个受保护的端点向 BFF 请求代理调用 API,BFF 负责附加 Token。

七、 最佳实践与总结

  • 优先使用服务器端设置的 Cookie: 特别是对于敏感信息如会话 ID 或 Token。
  • 始终为敏感 Cookie 设置 HttpOnlySecure 属性: 这是基本的安全要求。
  • 为会话 Cookie 设置 SameSite=Lax (或 Strict): 提供有效的 CSRF 防护。Lax 是兼顾安全和体验的良好默认值。
  • 仅在绝对必要时才使用 SameSite=None: 且必须配合 Secure,并确保有其他 CSRF 防护措施。
  • 避免在 Cookie 中存储大量或非必要的数据: 考虑其大小限制和每次请求都发送的开销。
  • 如果 Token 必须由客户端 JS 读取(如用于 Authorization 头):
    • localStorage 是常用选择,实现简单,但必须意识到并采取措施缓解 XSS 风险(如 CSP、输入过滤、输出编码)。
    • 使用HttpOnly 的 Cookie 也可以让 JS 读取,但相比 localStorage 没有明显的安全优势,反而可能引入 CSRF 风险(需要依赖 SameSite),且可能导致 Token 重复发送(一次在 Cookie 头,一次在 Authorization 头),通常不推荐这种方式。

理解 Cookie 的工作机制及其各种属性,特别是安全相关属性,对于构建安全、可靠的 Web 应用程序至关重要。