Skip to content

Cookie、Session 与设备管理 ​

服务端 Session 让浏览器只持有一个随机会话凭据,账号、认证强度和撤销状态留在服务端。它适合自有网站,因为“当前设备退出”“撤销其他设备”“改密后重新登录”都能映射成服务端状态变化。

浏览器接收 Set-Cookie 后,按域、路径、安全属性和上下文决定是否发送 Cookie。服务端收到 Cookie 再查询 Session;浏览器里存在一个 Cookie,并不说明对应会话仍有效。

下图把首次建立会话与后续请求分开。只有全部认证因素完成后才签发正式会话,后续请求仍检查服务端状态。

存储不可用时进入受控故障路径,不按“会话有效”处理;缓存带来的状态延迟见后文。

下面是 HTTPS 站点的响应示意。<random-session-token> 需要替换为密码学安全随机数;示例的 8 小时只是 Cookie 保存时长,不代表推荐的通用会话策略。

http
HTTP/1.1 200 OK
Set-Cookie: __Host-session=<random-session-token>; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=28800
Cache-Control: no-store
Content-Type: application/json

{"authenticated":true}

根据 MDN Set-Cookie:

属性作用与边界
Secure限制 Cookie 经安全连接发送,不替代全站 HTTPS
HttpOnly阻止脚本直接读取 Cookie;XSS 仍可能代用户发请求
SameSite=Lax限制部分跨站携带行为,仍需独立 CSRF 防护
Path=/控制发送路径,不能用作应用间安全隔离
不设置 Domain将 Cookie 限于当前主机
__Host- 前缀支持该前缀的浏览器要求 Secure、Path=/ 且无 Domain
Max-Age浏览器保存上限,服务端必须独立检查失效条件

跨源与跨站是不同概念。app.example.com 与 api.example.com 跨源,但在相同 scheme 等条件下可能同站;SameSite 无法替代对不可信同站子域的防护。

会话凭据与记录 ​

会话凭据应不可预测,不编码邮箱、IP 或用户 ID。可使用 32 字节安全随机数,编码后放进 Cookie;这是设计选择。数据库保存其 SHA-256 摘要,以降低只读会话库泄露直接产生可用会话的风险。该处理适用于高熵随机凭据,不适用于低熵密码。

设备列表使用独立的非秘密 session_id,避免把 Cookie 中的凭据作为列表 ID 或日志字段。

字段含义
session_id、token_digest管理标识与凭据摘要
user_id、security_version账号及创建时的安全版本
created_at、last_seen_at创建与最近活动时间
idle_expires_at、absolute_expires_at空闲与绝对过期边界
reauthenticated_at、auth_methods近期认证与认证方式
revoked_at、revoke_reason撤销状态与原因
设备描述、网络摘要给用户识别设备,不能单独证明设备可信

不要把 IP 或 User-Agent 固定匹配作为唯一有效性条件。移动网络和浏览器升级都会改变它们,攻击者也可能伪造;更适合用作风险信号。

登录、轮换与过期 ​

登录前的匿名会话与登录后的受信会话要轮换,防止会话固定攻击。权限提升、重要身份变化后也应重新评估是否轮换凭据。OWASP Session Management给出了会话标识更新和服务端超时要求。

每次请求至少检查会话存在、未撤销、未超时、账号可用、安全版本匹配。滑动续期只能推进空闲期限,不能越过绝对期限;敏感操作还要检查近期认证。

记录活动时间不必每个请求都写库。可以按时间粒度合并写入,但须约定空闲时间的误差上限;缓存续期与数据库持久状态也要遵守相同的绝对上限。否则持续活跃的会话可能永不结束。

凭据轮换要考虑并发请求:旧凭据立即删除会使尚在途的请求失败,长时间双有效则扩大泄露窗口。可以选择接受短暂失败、限制性过渡或按会话版本串行更新,但要验证旧响应晚到时不能把浏览器覆盖回已失效凭据。

多设备退出 ​

“设备”通常只是产品展示单位,一个浏览器的两个 Profile 可能对应两个 Session;同一台机器也可能有多个账号会话。

操作服务端动作
退出当前会话撤销当前记录并清除 Cookie
退出指定设备验证目标会话属于当前用户后撤销
退出其他设备撤销同用户除当前实例外的记录
退出全部设备更新安全版本或撤销所有会话,并清除当前 Cookie
修改密码或封禁根据产品策略处理当前会话,同时撤销旧认证状态

退出使用带 CSRF 防护的状态变更请求,避免 GET 链接产生退出副作用。清 Cookie 只是清理客户端持有物,不能替代服务端撤销。删除 Cookie 时使用相同名称、路径及域范围;__Host- Cookie 不设置 Domain。

“撤销成功”通常意味着后续通过认证检查的请求被拒绝,不能自动回滚此前已进入业务事务的操作。转账等敏感写入应在提交前检查必要状态,或使用业务侧的版本约束。

缓存一致性与故障 ​

如果服务 A 缓存 Session 30 秒,而服务 B 立即删除中心记录,A 仍可能接受旧会话。设计中应明确撤销生效的最坏时限:缓存剩余时间、事件传播和在途请求都可能贡献延迟。

按业务风险可以选择每次查询权威状态、短期缓存加可靠失效通知,或只在敏感写入重新查询。依赖不可用时,受保护操作不应降级成匿名放行;可返回明确的临时服务错误。是否允许已缓存状态继续支持低风险只读操作,需要独立的风险与时限策略。

若从旧备份恢复会话库,旧会话可能重新出现。恢复方案应验证撤销版本、全局失效代次与密钥状态,不能仅以“Redis 已恢复”作为完成条件。

跨域部署与登录回调 ​

跨源 fetch 发送 Cookie 需要正确的凭据模式和服务端 CORS 配置;允许携带凭据时不能把允许来源设置成任意通配符,详见 MDN CORS。CORS 控制浏览器能否向脚本暴露响应,不替代认证或 CSRF 防护。

OIDC 顶层 GET 回调通常更容易与 SameSite=Lax 配合;form_post 等跨站 POST 回调需要单独验证关联 Cookie 的发送行为。不要为修复回调丢状态而全站放宽 Cookie,或在丢失状态时跳过校验。

参考资料 ​