本文整理自 MDN 官方文档《HTTP Cookie》,梳理 Cookie 的原理、属性、安全与隐私问题。
一、HTTP Cookie 是什么
HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据。
浏览器会存储 Cookie,并在下次向同一服务器再发起请求时携带并发送到服务器。通常它用于告知服务端两个请求是否来自同一浏览器——如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为可能。
Cookie 主要用于以下三个方面:
- 会话状态管理——如用户登录状态、购物车、游戏分数或其他需要记录的信息
- 个性化设置——如用户自定义设置、主题和其他设置
- 浏览器行为跟踪——如跟踪分析用户行为等
二、创建 Cookie:Set-Cookie 和 Cookie 标头
服务器收到 HTTP 请求后,可以在响应标头中添加一个或多个 Set-Cookie 选项;浏览器收到响应后通常会保存下 Cookie,并将其放在 HTTP Cookie 标头内,向同一服务器发出请求时一起发送。你可以指定过期日期或时间段、对指定的域和路径设置额外限制,以控制 Cookie 的发送范围。
一个简单的 Cookie 长这样:
Set-Cookie: <cookie-name>=<cookie-value>服务器通过响应标头告知客户端存储一对 Cookie:
HTTP/1.0 200 OKContent-type: text/htmlSet-Cookie: yummy_cookie=chocoSet-Cookie: tasty_cookie=strawberry
[页面内容]之后,对该服务器发起的每一次新请求,浏览器都会将之前保存的 Cookie 信息通过 Cookie 请求头部再发送给服务器:
GET /sample_page.html HTTP/1.1Host: www.example.orgCookie: yummy_cookie=choco;tasty_cookie=strawberry三、Cookie 的生命周期
Cookie 的生命周期可以通过两种方式定义:
- 会话期 Cookie——在当前的会话结束之后删除。浏览器定义了”当前会话”结束的时间,一些浏览器重启时会使用会话恢复,这可能导致会话 Cookie 无限延长
- 持久性 Cookie——在过期时间(Expires)指定的日期或有效期(Max-Age)指定的一段时间后被删除
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;四、限制访问 Cookie:Secure 和 HttpOnly
有两种方法可以确保 Cookie 被安全发送,并且不会被意外的参与者或脚本访问:Secure 属性和 HttpOnly 属性。
Secure 属性
- 标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端
- 它永远不会使用不安全的 HTTP 发送(本地主机除外),这意味着中间人攻击者无法轻松访问它
- 不安全的站点(在 URL 中带有
http:)无法使用 Secure 属性设置 Cookie - 但 Secure 不会阻止对 Cookie 中敏感信息的访问——有权访问客户端硬盘的人(或未设置 HttpOnly 时的 JavaScript)仍可以读取和修改它
HttpOnly 属性
- JavaScript 的 Document.cookie API 无法访问带有 HttpOnly 属性的 Cookie,此类 Cookie 仅作用于服务器
- 例如,持久化服务器端会话的 Cookie 不需要对 JavaScript 可用,而应具有 HttpOnly 属性
- 此预防措施有助于缓解跨站点脚本(XSS)攻击
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly五、定义 Cookie 发送的位置:Domain / Path / SameSite
Domain 和 Path 标识定义了 Cookie 的作用域,即允许 Cookie 发送给哪些 URL。
1. Domain 属性
Domain 指定了哪些主机可以接受 Cookie:
- 不指定时,默认为同一 host 设置 Cookie,不包含子域名
- 指定了 Domain 时,一般包含子域名,限制更少;当子域需要共享有关用户的信息时,这会有所帮助
2. Path 属性
Path 属性指定了一个必须存在于请求 URL 中的路径,以便发送 Cookie 标头。以字符 /(%x2F)作为路径分隔符,并且子路径也会被匹配。
例如,设置 Path=/docs,以下地址都会匹配:
/docs/docs//docs/Web//docs/Web/HTTP但以下地址不会匹配:
//docsets/fr/docs3. SameSite 属性
SameSite 属性允许服务器指定是否/何时通过跨站点请求发送 Cookie(站点由注册的域和方案定义:http 或 https),提供针对**跨站点请求伪造攻击(CSRF)**的保护。它采用三个可能的值:Strict、Lax 和 None:
- Strict——Cookie 仅发送到它来源的站点
- Lax——与 Strict 相似,但在用户导航到 Cookie 的源站点时(如通过跟踪来自外部站点的链接)会发送 Cookie
- None——浏览器会在同站请求和跨站请求下继续发送 Cookie,但仅在安全的上下文中(即如果
SameSite=None,则还必须设置 Secure 属性,否则浏览器会拒绝该 Cookie)
六、SameSite 大白话讲解
SameSite 就是浏览器给 Cookie 加的一个「跨站访问门禁」。
- 你在 A 网站登录后,浏览器会存 A 网站的 Cookie(相当于你的「家门钥匙」)
- 正常情况:你访问 A 网站,浏览器自动把钥匙给 A,没问题
- 风险情况:你点了一个恶意网站 B 的链接,B 偷偷给 A 网站发请求,如果浏览器把 A 的钥匙给了 B,B 就能冒充你操作 A 网站(这就是 CSRF 攻击)
- SameSite 的作用:规定「什么时候能把钥匙给别人」,防止坏人偷用你的钥匙
1. Strict(最严:只给自家,外人一概不给)
人话翻译:我的钥匙,只有我自己开门能用,别人碰都别想碰。
- 规则:只有「完全同站」的请求才会带 Cookie
- 同站:域名、协议(http/https)完全一样,比如你在
https://a.com访问https://a.com/xxx - 跨站:从
https://b.com跳转到https://a.com,哪怕是点链接,也绝对不带
- 同站:域名、协议(http/https)完全一样,比如你在
- 生活类比:你家门钥匙,只有你自己掏出来开门能用,朋友、快递员、陌生人谁都不能用你的钥匙开门
- 适用场景:银行、支付、核心账号操作等极高安全要求的场景
- 缺点:太严了!比如你在微信里点朋友发的银行链接,打开后会直接显示「未登录」,因为 Strict 不让跨站带 Cookie
2. Lax(宽松:自家能用,安全的跨站也能用,危险的不给)
人话翻译:我的钥匙,自己开门能用,正常的「点链接跳转」也能用,但偷偷摸摸的请求绝对不给。
- 规则:同站请求必带,安全的跨站请求(GET 跳转)带,危险的跨站请求(POST 提交、iframe 嵌入、AJAX 请求)绝对不带
- 允许带:你点了
https://b.com页面里的<a href="https://a.com">链接,跳转到 A 网站,会带 Cookie(正常的用户主动跳转) - 禁止带:B 网站偷偷用
<form action="https://a.com/transfer" method="POST">给 A 发转账请求,绝对不带 Cookie,转账失败
- 允许带:你点了
- 生活类比:自己开门能用,朋友给你递钥匙(正常点链接)也能用,但陌生人偷偷配钥匙、撬锁(偷偷发请求)绝对用不了
- 适用场景:绝大多数网站的默认选择(现在浏览器已经把 Lax 设为 Cookie 的默认 SameSite 值)
- 优点:安全 + 体验平衡,既防 CSRF,又不影响正常的跨站跳转登录
3. None(无限制:谁要都给,必须加 Secure)
人话翻译:我的钥匙,谁要都给,但必须走「安全通道」(HTTPS)。
- 规则:不管是同站还是跨站,所有请求都带 Cookie,但强制要求必须是 HTTPS 协议(必须加 Secure 属性,否则浏览器直接拒绝)
- 生活类比:谁要都给,但只能在「有保安的安全通道」里递钥匙,绝对不能在大街上随便给
- 适用场景:必须跨站共享 Cookie 的场景,比如:
- 第三方登录(微信 / QQ 登录第三方网站)
- 跨域 iframe 嵌入(比如把 A 网站的支付页面嵌到 B 网站里)
- 前后端分离的跨域请求(但现在更推荐用 JWT 存 Cookie 加 Lax/Strict)
- 风险:如果不加 Secure,等于把钥匙随便给所有人,CSRF 风险极高;现在浏览器已经不允许 HTTP 下用
SameSite=None
Set-Cookie: mykey=myvalue; SameSite=Strict七、Cookie 前缀:__Host- 与 __Secure-
Cookie 的机制使得服务器无法确认 Cookie 是在安全来源上设置的,甚至无法确定 Cookie 最初是在哪里设置的。子域上的易受攻击的应用程序可以使用 Domain 属性设置 Cookie,从而访问所有其他子域上的该 Cookie,在会话劫持攻击中可能会被滥用。
__Host-前缀——仅当 Cookie 同时满足以下条件时,才在 Set-Cookie 标头中被接受:用 Secure 属性标记、从安全来源发送、不包括 Domain 属性、Path 属性设置为/。这样这些 Cookie 可以被视为”domain-locked”(域锁定)__Secure-前缀——仅当 Cookie 用 Secure 属性标记且从安全来源发送时,才在 Set-Cookie 标头中被接受。该前缀限制弱于__Host-前缀
八、JavaScript 访问 Cookie:Document.cookie
通过 Document.cookie 属性可创建新的 Cookie。如果未设置 HttpOnly 标记,你也可以从 JavaScript 访问现有的 Cookie:
document.cookie = "yummy_cookie=choco";document.cookie = "tasty_cookie=strawberry";console.log(document.cookie);// logs "yummy_cookie=choco; tasty_cookie=strawberry"九、安全
缓解涉及 Cookie 的攻击的方法:
- 使用 HttpOnly 属性防止通过 JavaScript 访问 Cookie 值
- 用于敏感信息(例如指示身份验证)的 Cookie 的生存期应较短,并且 SameSite 属性设置为 Strict 或 Lax。在支持 SameSite 的浏览器中,这样可确保不与跨站点请求一起发送身份验证 Cookie,此类请求实际上不会向应用服务器进行身份验证
十、跟踪和隐私:第三方 Cookie
Cookie 与特定域和方案(例如 http 或 https)相关联,如果设置了 Set-Cookie Domain 属性,也可能与子域相关联:
- 如果 Cookie 的域和方案匹配当前页面,则认为 Cookie 和页面来自同一站点,称为第一方 Cookie(first-party cookie)
- 如果域和方案不同,则称为第三方 Cookie(third-party cookie)
托管网页的服务器设置第一方 Cookie,但页面可能包含存储在其他域服务器上的图像或其他组件(例如广告横幅),这些组件可能会设置第三方 Cookie——它们主要用于网络上的广告和跟踪(例如 Google 使用的 Cookie 类型)。
总结:Cookie 属性速查表
| 属性 | 作用 | 关键点 |
|---|---|---|
Expires / Max-Age | 定义生命周期 | 会话期 vs 持久性 |
Secure | 仅通过 HTTPS 发送 | 防中间人窃听 |
HttpOnly | 禁止 JavaScript 访问 | 防 XSS 窃取 |
Domain / Path | 定义发送作用域 | 子域、子路径匹配 |
SameSite | 控制跨站发送 | Strict / Lax / None,防 CSRF |
__Host- / __Secure- | 安全前缀 | 强制 Secure、域锁定 |
资料来源:MDN — HTTP Cookie
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
最后更新于 ,距今已过 158 天
部分内容可能已过时