安全测试-04-登录、会话与权限模型的安全验证方法

做安全测试时,会把注意力优先放在明显的漏洞名字上,比如:

  • XSS
  • SQL 注入
  • 文件上传绕过

这些当然重要,但如果从业务系统的真实风险角度看,更常优先盯的是三件更基础的事:

  • 登录链路稳不稳
  • 会话状态管得严不严
  • 权限模型是不是只停留在页面层

因为很多问题最后都不是“没做登录”,而是:

  • 登录成功后身份边界没有守住
  • 退出、改密、角色变更后旧会话还活着
  • 页面按钮控住了,但服务端动作权限没有控住

这篇文章就聚焦这三条主线,讲一套更贴近业务测试的验证方法。

一、为什么这三件事最值得优先测

如果一个系统有下面这些特征,那登录、会话和权限模型就一定值得先看:

  • 有多角色,比如普通用户、审核员、管理员
  • 有审批、退款、封禁、授权这类高风险动作
  • 有导出、下载、查看详情这类数据边界动作
  • 有移动端、Web、后台多端共存
  • 有单点登录、角色切换、组织架构同步

因为只要这些东西存在,你就很难绕开几个问题:

  • 当前登录身份到底是如何建立的
  • 登录后身份到底用什么维持
  • 一个动作到底由谁说了算
  • 身份变化后,旧权限是否还残留

二、登录安全最值得看的不是“能不能登录”,而是“怎么登录成功的”

很多测试把登录验证停留在:

  • 正确账号密码能登录
  • 错误密码不能登录

这远远不够。

更关注登录链路里的几个问题:

  • 登录成功凭证是什么
  • 凭证是否只在正确条件下发放
  • 登录失败时是否有边界保护
  • 登录态是否和设备、时间窗、风险动作有关联

1. 先明确系统用什么维持身份

常见做法包括:

  • Session + Cookie
  • Access Token + Refresh Token
  • 单点登录票据
  • 自定义签名 + 会话态

这一步必须先弄清楚。
因为如果连身份载体是什么都不清楚,后面的会话验证很容易测偏。

2. 再看登录失败有没有最基本边界

例如:

  • 连续输错密码有没有限制
  • 验证码是不是形同虚设
  • 异常频率登录有没有风控
  • 同一账号在异常时间窗里是否有保护

这类问题不一定都要做到风控级别,但至少不能完全裸奔。

3. 再看登录成功后有没有不合理放宽

例如:

  • 登录后长期不失效
  • 高风险操作不做二次校验
  • 异地登录、设备切换没有任何提醒和限制

这类问题功能上常常“完全没错”,但安全上边界会很松。

三、会话管理最容易出问题的 5 个地方

很多系统问题不是出在登录,而是出在登录之后。

最常检查的会话问题有这 5 类。

1. 退出登录后旧会话是否真正失效

这个是最基本的。

验证方式通常很简单:

  1. 先正常登录并抓取一个关键请求
  2. 执行退出登录
  3. 用退出前的原请求直接重放

如果还能继续查数据、改数据、做动作,就说明会话失效机制有问题。

2. 修改密码后旧会话是否仍然可用

这个问题经常被漏掉。

因为很多系统只更新了密码,却没有同步失效所有旧 Session 或旧 Token。

结果就是:

  • 密码虽然变了
  • 但原设备、原浏览器、原脚本还能继续访问

3. 角色变化后旧权限是否还保留

例如:

  • 管理员被降权为普通用户
  • 审批人被从当前节点移除
  • 用户部门变更导致数据权限收窄

这时如果旧 Token 还能继续拿着原权限操作,就是典型问题。

4. 多端登录、多设备登录的状态是否一致

例如:

  • Web 登录后,App 旧会话是否继续有效
  • 新设备登录后,旧设备是否应该下线
  • 管理后台和业务前台是否共用同一会话体系

这类问题最怕的是系统行为不一致,导致某些端口会话异常宽松。

5. 敏感动作是否需要额外确认

例如:

  • 修改手机号
  • 重置密码
  • 提现、退款
  • 权限分配

登录态存在,不代表所有敏感动作都可以直接做。
很多系统需要补一层:

  • 短信校验
  • 二次密码
  • 一次性确认 token

如果完全没有,风险通常会比较高。

四、权限模型最容易测偏的地方:把“按钮权限”当成“动作权限”

权限模型验证里,最常见的误区是:

  • 普通用户看不到按钮
  • 所以普通用户一定做不了

这完全不成立。

真正要验证的是:

  • 当前用户有没有权查看某资源
  • 当前用户有没有权修改某资源
  • 当前用户有没有权执行某动作
  • 这些判断是不是都在服务端重新做过

1. 先把权限拆成三层

通常可以按下面三层去看:

  • 页面权限:能不能看到入口
  • 数据权限:能不能看到这份数据
  • 动作权限:能不能对这份数据执行操作

很多系统只做了第一层,第二层和第三层其实很空。

2. 再把角色和资源关系理清楚

至少要明确:

  • 哪些角色能访问哪些模块
  • 哪些角色能看哪些数据范围
  • 哪些角色能做哪些动作
  • 哪些动作还要叠加资源归属关系

例如“审批人可以审批”,这句话本身还不够。
还必须补一句:

只能审批当前分配给自己的任务。

3. 最后用实际账号做交叉验证

至少准备:

  • 普通用户 A
  • 普通用户 B
  • 审核员或主管 C
  • 管理员 D

然后验证:

  • A 能不能看 B 的数据
  • A 能不能做 C 的动作
  • C 能不能看不属于自己流程节点的数据
  • D 的高权限接口是否真的只有 D 可用

五、一套可执行的最小验证骨架

如果你要把这块长期做稳,建议至少固定下面这套最小骨架。

1. 登录链路检查表

检查项 目标
正常登录 确认正确身份建立
错误密码多次尝试 确认失败边界
验证码/风控 确认异常登录是否有限制
异常设备/异常时间 确认是否有额外保护
登录成功凭证获取 明确后续会话验证对象

2. 会话失效检查表

场景 验证动作 预期
退出登录 重放旧请求 应拒绝访问
修改密码 重放旧请求 应拒绝访问
角色降权 重放高权限请求 应拒绝访问
新设备登录 旧设备继续请求 依据产品策略校验

3. 权限模型检查表

接口/动作 普通用户 A 普通用户 B 审核员/管理员 关注点
查看详情 只能看自己 只能看自己 视角色策略而定 数据隔离
修改资料 只能改自己 只能改自己 可受限修改 资源归属
审批通过 不可执行 不可执行 可执行 动作权限
导出数据 受范围限制 受范围限制 可扩展范围 数据边界

这三张表一旦固定下来,后面每个新模块都可以直接套。

六、测试时最容易踩的几个坑

1. 只测登录成功,不测身份变更后的残留问题

很多问题都发生在:

  • 退出后
  • 改密后
  • 降权后
  • 切角色后

如果只测“能不能登录”,问题会漏很多。

2. 只测页面,不测接口

页面层很容易显得“权限控制很好”,但真正动作往往还得在接口层重放一次才知道。

3. 不准备干净的角色和资源关系

如果账号、部门、审批链、数据归属关系本来就没配清楚,最后很容易把共享数据误判成越权。

4. 忽略多端和多系统边界

很多系统问题不是单点接口的问题,而是:

  • Web 端失效了
  • App 端没失效
  • 后台系统降权了
  • 前台接口缓存的旧权限还在

七、真实案例:为什么管理员被降权后,旧浏览器还能继续删用户

场景

一个后台管理系统里,管理员账号可以执行高风险动作,比如:

  • 删除用户
  • 调整角色
  • 导出全量报表

某次组织调整后,一个管理员账号被降为普通运营角色。
按产品预期,这个账号之后只能做基础查询,不能继续删用户。

执行

先用管理员身份登录后台,抓到了删除用户接口:

1
2
3
4
5
6
7
POST /admin/user/delete HTTP/1.1
Cookie: session=abc123
Content-Type: application/json

{
"userId": 91827
}

然后做了这几步:

  1. 在后台把该账号角色降为普通运营
  2. 不刷新当前浏览器,直接重放原删除请求
  3. 再在新浏览器重新登录同一账号,对比两边行为

现象

结果非常典型:

  • 新浏览器重新登录后,删除按钮已经消失
  • 新会话直接调删除接口会返回无权限
  • 但旧浏览器里的原 Session 继续重放删除请求,仍然可以删除用户

这说明权限收回只影响了新登录会话,没有影响已存在的旧会话。

排查

顺着代码和日志看,问题链路是这样的:

  • 登录成功时,系统把角色权限快照写进了 Session
  • 后续接口校验直接使用 Session 里的角色信息
  • 角色变更时,只更新了数据库里的角色关系
  • 没有同步使旧 Session 失效,也没有在关键动作处重新校验当前角色

本质上是两个问题叠在一起:

  • 会话失效没收回来
  • 高风险动作没做实时权限校验

修复

最后给出的修复建议分成 4 条:

  1. 角色变化后主动失效该账号所有在线会话
  2. 高风险管理动作必须实时查当前权限,而不是只信 Session 快照
  3. 管理后台增加会话版本号或权限版本号校验
  4. 增加自动化回归用例:
  • 高权限账号登录
  • 中途降权
  • 旧请求重放
  • 预期高风险动作立即失效

这个案例很典型地说明了一点:

权限模型不只是“现在是什么角色”,还包括“旧身份什么时候必须失效”。

八、怎么把这类验证持续沉淀下来

更合适的做法是把这块沉淀成固定回归项,而不是靠人工临场判断。

第一层:上线前固定检查

高风险模块至少固定看:

  • 退出后旧会话是否失效
  • 改密后旧会话是否失效
  • 角色变化后旧权限是否失效
  • 关键动作是否做服务端动作权限校验
  • 数据权限是否按资源归属隔离

第二层:自动化回归

最值得固化成脚本的包括:

  • 退出登录后重放关键请求
  • 改密后重放关键请求
  • 降权后重放高权限请求
  • 普通账号重放审批、删除、导出等高风险动作

这些脚本不一定很复杂,但价值很高,因为这类问题非常容易被后续改动重新带回来。

九、写在最后

登录、会话和权限模型这三块之所以值得长期固定检查,是因为它们决定了业务系统最基本的身份边界。

说到底,这些问题最终都在回答同一件事:

  • 这个人到底是谁
  • 这个身份现在还算不算有效
  • 这个身份当前到底能做什么

如果这三个问题没有在系统里被严谨回答,前面看起来再完整的功能流程,安全上都可能是空的。