治理:角色会腐烂,权限只增不减
为什么:在 User 和 Permission 之间插一层 Role 给出的是模型正确的起点。真实系统烂掉不是因为模型错,而是因为它每周都在被改:新部门要一套权限、某个人要一个例外、上线前临时开一个口子。这一页讲的是让它别烂的几件事。
1 · 角色数的乘法
角色本该只描述职能。一旦部门、数据范围也被编进角色名,角色数就成了几个维度的乘积:3 个部门、3 种职能、2 档范围已经是 18 个角色,名字长成 editor-marketing-group。role explosion 的成因几乎总是同一个:把本该当参数传的东西塞进了角色本身。
注 · 本系列的 perm.ts 自己藏着一处相关的实现意外:ROLES 里 admin 的 grants 写作 CATALOG.map((p) => [p.key, p.scopes[0]])。这意味着往能力清单里新增一条 permission,admin 立刻拥有它,不经任何分配动作;而它拿到的 scope 是该条
scopes 数组的第一项——content:article:create 的 scopes 只写了 ['Self'],于是 admin 对新建文章的范围是 Self 而非 Full。清单数组的书写顺序于此成了实际策略,这类「顺序即策略」的写法在真实系统里同样常见,且不会有任何报错提醒。
2 · 继承边带来的意外提权
role hierarchy 让一个角色继承另一个角色的全部权限,senior-editor ⊃ editor 省掉一份重复清单。代价是提权路径变得不可见:给 reviewer 加一条「继承 editor」只是为了让它能读全部文章,editor 名下的 content:article:export 也跟着进来了,而导出恰恰是数据外泄风险最高的那类
action。
继承还会形成环。A ⊃ B、B ⊃ C、C ⊃ A 三条边各自看都合理,合起来让三个角色的权限集合坍缩成同一个。判定侧不会崩,只是所有人都比预期多了权限。
警示 · 继承的语义要在写下第一条边之前定死:A ⊃ B 是「A 拥有 B 的全部权限」还是「A 的成员自动成为 B 的成员」?两种读法在有用户级 Deny 时结论不同——前者把 B 的权限并进 A 再减 Deny,后者让用户同时持有两个角色、两套 Deny 各自生效。最终生效集
的公式只覆盖后者。
3 · 一个人不能既申请又审批
separation of duty 是审计侧最硬的一条约束,落到权限模型上有两种强度。静态形态限制持有:同一个用户不得同时拥有这两个角色,分配时就拦住。动态形态限制行使:两个角色都可以有,但在同一张单据上不得既 submit 又 approve,判定时才拦。
| 形态 | 拦在哪 | 判据 | 代价 |
|---|---|---|---|
| 静态 | 角色分配界面 | 用户的角色集合不含互斥对 | 小团队里常常无人可用 |
| 动态 | 每次审批判定 | 当前单据的 submitted_by ≠ me |
判定要读单据的历史字段 |
静态约束对小团队往往不可行:只有两个人的部门做不到申请与审批分离。动态约束更实用,代价是它把「谁提交的」这一历史事实变成了授权输入,而这条信息属于 what 那一轴的资源属性,不归 subject。
4 · 回收侧的缺口
权限的增长是单向的:开口子有人催,收口子没人提。三件事能把它拉回来。授予带到期时间,临时权限过期自动失效,而不是等人想起来删;高危 action 改为 just-in-time 申请,用完即收,把常态持有变成短时持有;break-glass 账号留给故障现场,平时锁死、启用即告警、全程录审计。
recertification 是最后一道:每季度把每个人的生效集发给他的直属负责人确认,未在期限内确认的自动收回。这条规则的威力全在「不确认就收回」——反过来做(不确认就保留)等于没做。
5 · 改动之前先算影响面
策略改动的评审对象不该是 diff,而该是这次改动翻转了哪些决策。做法是把请求空间枚举成一个样本网格,改动前后各跑一遍,比对结果。
本系列的 8 条规则、4 个 subject、4 个 resource、5 个 action 组成 80 条请求(均用默认 context),逐条停用规则测得的影响面里,r-mfa 与 r-abroad 都是 0 条:它们读的是设备与网络位置,而默认 context 恒为公司内网加托管设备。把 context 扩成 4 种、样本变成 320 条,同样两条规则的影响面成了
18 条与 11 条。
建议 · 影响面为 0 有两种截然不同的含义:改动确实无效,或者样本没覆盖到它。前者可以放心合并,后者是评审失效。因此样本网格的每一维都要显式列出取值,尤其 context 这一维:它不出现在任何一条 role 或 permission 的定义里,最容易在测试里被固定成一个值。
6 · 参考文献
- Ferraiolo, D. F., & Kuhn, D. R. (1992). Role-Based Access Controls. 15th NIST-NCSC National Computer Security Conference, 554–563. RBAC 与职责分离约束的原始论述。csrc.nist.gov
- Sandhu, R., Coyne, E. J., Feinstein, H. L., & Youman, C. E. (1996). Role-Based Access Control Models. IEEE Computer, 29(2), 38–47. RBAC0–RBAC3 四层模型,role hierarchy 与约束各占一层。
- American National Standards Institute. (2004). Information Technology — Role Based Access Control. ANSI/INCITS 359-2004. 把静态与动态职责分离写进标准的那一版。