会话的安全感不来自令牌有多长,而来自你能否在下一秒把它作废。
一、一次没有退出的登录
2026 年 8 月的一个下午,他在客户的会议室里用共享电脑演示任务板。演示结束,他合上笔记本就走,浏览器里的登录状态还留着。四个小时后,下一位使用者打开同一个标签页,直接看见了这个账号的全部项目、成员列表和导出按钮。
以下情境为教学假设,人物、引语和数字不代表经核实的真实案例。
他当天傍晚才发现:审计记录里多了一次不需要重新输入密码的导出。时间线很清楚,十四点零五分演示结束,十八点多那次导出发生,中间四个小时里,任何人都可以接着用这个已经登录的会话。
那天晚上他做的第一件事不是换框架,而是给会话装一个随时能关掉的开关:会话存服务端,登出时删掉那一行,删完立刻失效。代价是一张表和每次请求一次主键查询,收益是撤销延迟从「等令牌过期」变成零。
他把这个开关写进了三条规则:任何设备都能单独踢下线、改密码后踢掉全部设备、权限变更后让旧会话立刻重新校验。三条规则加起来不到四十行代码,却把「人已经走了,权限还在」这类事故挡在门外。
二、会话表与无状态令牌的取舍
他先把两种做法摆在一张表上,而不是先选库。表里的「撤销延迟」一列是决定性的一列,因为事故发生时,人能做的只有撤销。
| 维度 | 服务端会话表 | 无状态 JWT |
|---|---|---|
| 撤销延迟 | 0,删行即失效 | 最长 15 分钟,等令牌过期 |
| 每次请求成本 | 一次主键查询 | 一次本地签名校验 |
| 登出全设备 | 删该用户的全部会话行 | 需要额外的黑名单或版本号 |
| 服务端容量 | 619 个会话约 310 KB | 不占服务端存储 |
| 数据库故障时 | 全员暂时无法校验 | 校验仍可在本地完成 |
| 适合的场景 | 单体应用、需要即时撤销 | 跨服务调用、极短时效 |
他选了会话表,理由只有一个:这个产品里最贵的动作是「把人赶出去」。 客户离职、设备丢失、共享电脑忘记退出,这三件事都要求撤销在下一秒生效;等十五分钟的令牌过期,在这三个场景里都等于没有撤销。
JWT 不是错的,错的是把它当会话用。 无状态令牌适合服务之间的短时调用,最长十五分钟的撤销延迟在那种场景里可以接受;一旦拿它承载浏览器登录态,你就必须自己补一套黑名单,最后又回到服务端存储。
混合方案他没有采用。 同时维护会话表和令牌黑名单,意味着两套过期逻辑、两处撤销入口;一个人的产品里,多一套状态就是多一个会忘记清理的角落。
过期与撤销是两件事,不能互相替代。 过期解决「令牌活得太久」,撤销解决「令牌不该再活」;只有过期没有撤销,事故期间你只能看着它继续有效。
会话行里要留下能解释的字段。 创建时间、最后活动时间、设备摘要、权限版本,四个字段足够在复盘时说清「谁在什么时候从哪里进来」。
会话 ID 用 32 字节随机数,不用自增主键。 随机数让「猜下一个会话」这件事没有着力点;自增主键省不了多少空间,却把枚举变成了一个可以写脚本的动作。
会话表只存哈希,不存原始 ID。 数据库被读走时,攻击者拿到的是一串哈希,无法直接拿来冒充;校验时对请求里的 ID 做一次哈希再比对,成本是一次内存里的运算。
三、三十天、十四天与零秒
三个数字定下之后,会话的行为就不再靠感觉。他给每个数字都写了「为什么是这个值」和「失败时会发生什么」。
| 参数 | 取值 | 为什么 | 设置失误的后果 |
|---|---|---|---|
| 绝对过期 | 30 天 | 超过一个月不登录的设备,多半已经不用了 | 长期不用的设备一直持有有效会话 |
| 空闲过期 | 14 天 | 两周没有操作,人大概率已经离开这个场景 | 共享电脑上的会话可以无限续命 |
| 撤销延迟 | 0 秒 | 删行即失效,不等任何缓存 | 踢下线要等令牌过期,事故窗口被拉长 |
绝对过期从登录那一刻算,不随活动续期。 这条规则让「一直开着标签页」也无法把会话续到一年;到期就重新登录,成本是三十天一次,换来的是会话不会永久存在。
空闲过期从最后一次请求算,十四天。 它和绝对过期是两条独立的线,先到先失效;两条线都设,是因为它们防的不是同一件事:一条防「设备被遗忘」,一条防「人已离开但页面还开着」。
撤销延迟零秒靠的是每次请求都查库。 他试过在内存里缓存会话,登录页的响应快了一点,但踢下线要等缓存过期;这点收益不值得,缓存被删掉了。
619 个活跃会话不是拍脑袋的数。 344 个账号,平均每人 1.8 台设备,乘出来 619.2,取整 619;每个会话约 0.5 KB,整张表 310 KB 左右。
1.8 台设备是平均值,不是上限。 有人在手机、平板和两台电脑上同时登录,单个账号的会话数会明显高于平均值;容量按平均值算,风控要按最大值想。
310 KB 说明容量不是问题,索引才是。 会话表按 user_id 建索引,登出全设备就是一条按用户删除的语句;没有这个索引,一次全设备登出会扫全表,人越多越慢。
会话表也要有清理任务。 每天删一次已经过期的行,表不会无限增长;清理失败不影响登录,但会在几周后表现为查询变慢。
会话数量要能被看见。 他在账号设置里放了一个「已登录设备」列表,显示设备、最近活动时间和当前会话标记;用户能自己踢掉不认识的设备,比等他发现日志要快得多。
四、四个小时与十六个窗口
把这次事故的账算一遍,就能看出「等过期」和「即时撤销」差在哪里。
复算:
活跃会话 = 344 账号 × 1.8 设备 = 619.2 → 619 个
存储占用 = 619 × 0.5 KB = 309.5 KB ≈ 310 KB
每账号平均会话 = 619 ÷ 344 = 1.8 个
暴露时长 = 4 小时 = 240 分钟
无状态 JWT(撤销延迟上限 15 分钟):240 ÷ 15 = 16 个窗口
服务端会话(撤销延迟 0 秒):发现即失效,窗口 = 0
十六个窗口的意思是,在最坏情况下有十六次机会让旧令牌继续通过校验。 这不是说 JWT 一定被利用,而是说撤销动作在这四个小时里没有任何一次真正生效;服务端会话把这段窗口压成一次请求的时间。
窗口数量只是上界,不是命中次数。 旧令牌要在这十六个窗口里被使用才会产生实际影响;但安全设计不能假设「没人会用」,只能假设「一定会被用」。
四个小时的暴露不全是技术问题。 共享电脑上的登录没有退出,首先是使用习惯;但如果系统支持「查看已登录设备并一键踢掉」,习惯的代价可以从四小时降到一次点击。
会话表让「改密码」有了明确含义。 改密码时删掉该用户全部会话,旧设备下一次请求就被弹回登录页;如果只改密码不删会话,用户会以为安全了,实际旧设备还能用。
权限变更也要碰会话。 把成员从管理员降为普通成员后,旧会话里的角色缓存必须失效;他的做法是会话行里存一个权限版本号,权限一变就递增,请求时版本不符即重新加载。
会话过期不等于数据过期。 会话删掉只是让人重新登录,已经导出的数据不会因此回收;这两件事的边界要在隐私说明里分别写清楚。
空闲过期的实现要防「后台轮询续命」。 如果前端定时拉取通知,空闲过期永远不会触发;他的做法是只有用户主动操作才算活动,轮询请求不刷新最后活动时间。
三十天绝对过期快到时要提前提示。 临近到期时在界面顶部显示一次「登录即将过期」,避免用户在写一半的任务描述时被弹回登录页;提示本身不续期,只是让人有准备。
五、行动清单与复算验收
按顺序做,会话行为就会从「默认配置」变成「写下来的规则」。
- 统计当前活跃会话数与每账号平均设备数,算出会话表占用。
- 给会话表加
user_id索引,验证一次全设备登出所需的查询计划。 - 设定绝对过期 30 天与空闲过期 14 天,并确认两者独立生效。
- 把登出实现为删行,验证删除后旧会话的下一次请求被拒绝。
- 在账号设置里加「已登录设备」列表与单设备踢下线入口。
- 改密码与权限变更时,强制失效该用户的全部旧会话。
- 确认前端轮询不会刷新最后活动时间,避免空闲过期失效。
- 用一次测试账号走完「登录、换设备、踢下线、改密码」四条路径并记录耗时。
验收标准:你能在三十秒内让任意一个会话失效,并能说清绝对过期与空闲过期各自防的是什么;做不到,会话就还处在「等它自己过期」的状态。
前四条是机制,后四条是让人看得见机制。如果只有半天,先做第一条和第四条;没有会话清单,后面的设备管理和过期策略都无从验证。
清单不要一次全做。第一周只做统计与索引,第二周加设备列表,第三周才改过期参数;一次改太多,出问题时分不清是哪一条引起的。
使用边界与实操验收
会话表不是唯一答案。 它适合单体应用和需要即时撤销的场景;如果产品已经拆成多个服务,会话校验可能要走一次内部调用,那时再评估短时效令牌加撤销名单。
三个数字是教学取值,不是行业标准。 30 天与 14 天取自本组基线,换一个产品、换一类用户,取值会不同;重要的是两个数字都写下来,并且能说出失败时的后果。
零秒撤销依赖每次请求查库。 数据库不可用时,所有人都会被挡在门外;这是有意的取舍,因为「暂时进不来」比「该出去的人出不去」更容易解释,也更容易恢复。
设备列表会暴露一部分隐私。 列表里只显示设备类型、浏览器和最近活动时间,不显示精确位置;显示得越细,越容易变成另一种信息泄露。
本文只讨论会话与令牌的生命周期。 数字全部是教学假设,不构成对任何框架、库或云服务的推荐;具体实现以你所选技术栈的官方文档为准。
关键参考与核验范围
- OWASP Session Management Cheat Sheet :用于核对会话 ID 的生成、过期与登出后的处理要求。
- NIST SP 800-63B :用于核对认证器与会话生命周期中与保证等级相关的要求。
- OWASP ASVS :用于核对会话管理相关的验收条目,本文不逐条对照。
- OWASP Top Ten :用于说明身份认证与会话失效这一类风险的常见形态。
以上链接于 2026-10-26 实测可公开访问;未核验版本、条款编号与具体建议。核验日期:2026-10-26。
边界句。 本文只讨论一人产品的会话存储、过期与撤销,不讨论单点登录、多因素认证的产品选型与合规审计。
系列导航
- 上一篇:认证:密码、魔法链接、OAuth 与一次账号枚举
- 下一篇:密钥与配置:十七个密钥、三档与泄露后的三十分钟
- 回到目录:一个人的安全基线:十四个入口、十七个密钥与一次 72 小时响应
- 相关阅读:API 密钥与泄露:一把钥匙的存放、轮换与吊销
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。