Havenlon | From Access to Execution #02 Least Privilege vs Least Execution Authority
Least Privilege 是安全工程中最成熟的原则之一。Saltzer 与 Schroeder 在 1975 年给出的表述已经相当完整:每一个程序、每一个用户都应当以完成当前工作所必需的最小特权集合运行,不因为组织身份、历史关系或管理便利而额外持有与任务无关的能力。半个世纪以来,这条原则被写进了几乎所有身份与访问管理体系,也确实极大地降低了凭据泄露、身份冒用与横向移动的风险。一个只需要调用支付接口的 Agent 不应该同时持有数据库管理员权限;一个只需要读取某个存储桶的自动化流程不应该获得整个云账户的写权限。这些判断到今天依然完全正确。
值得注意的是这条原则从一开始就确定下来的计量单位:privilege。它衡量的是主体持有的能力集合,而不是这些能力可能产生的后果集合。在软件主要用于读取信息、生成内容、修改数据库记录的年代,这两者的差距通常不构成独立的安全问题——把能力砍到最小,后果自然也就被砍到最小。
而当机器主体开始自主发起支付、变更基础设施、部署生产系统、控制物理设备之后,这两个集合之间的距离开始被拉开。一个权限可以已经被压缩到不能再小,它背后映射的现实能力仍然可能很大。Least Privilege 与 Least Execution Authority 的分歧就从这里开始,而这不是"传统权限模型不够细"的问题,是两者在测量不同的东西。
一、Least Privilege 缩小可调用的能力集合,Least Execution Authority 缩小可到达的现实状态集合
传统最小权限要回答的问题是:一个主体为了完成任务,究竟需要哪些 Permission。如果某个 Agent 只需要 payment.execute、cluster.restart、deployment.create,那么系统就不应该再额外给它 payment.admin、cluster.delete、iam.manage 或 database.owner。这个过程收缩的对象是集合本身:
主体 → 最小必要 Permission Set
但即使收缩到只剩一个权限,这个权限背后仍然可能对应一个巨大的结果空间。payment.execute 可以是转出十美元,也可以是转出十万美元;可以转给一个已在册的供应商,也可以转给一个五分钟前才创建的账户;可以一天执行一次,也可以一秒执行几十次;可以在业务状态正常时执行,也可以在风控状态已经变化之后继续执行。从 IAM 的视角看,这些都是同一个权限的合法使用;从现实后果的视角看,它们根本不是同一件事。
把这个差别形式化,就得到本篇真正要建立的一组对照。Least Privilege 优化的是 Permission Set:主体持有哪些操作能力。Least Execution Authority 优化的是 Reachable State Space:凭借这些能力,主体最终能把受保护的现实带到哪些状态。
两个 Agent 可以持有完全相同的 infrastructure.modify,其中一个被限定为只能重启单个非生产节点,另一个则可以改写整个生产集群的配置。以权限集合度量,二者几乎等价;以可达状态空间度量,二者相差数个数量级。真正需要被最小化的显然是后者,但最小权限原则本身并不产出这个度量。
Least Privilege 约束主体能调用什么;Least Execution Authority 约束主体最终能把现实改变到什么程度。
二、权限的切分依据与风险的切分依据来自两套不同的设计动机
同一个权限对应不同风险,这个现象本身不难观察:
| Permission | 具体执行 | 现实后果 |
|---|---|---|
| payment.execute | 向已验证供应商支付 500 | 可逆,损失可控 |
| payment.execute | 向新建账户支付 50,000 | 部分可逆,需要人工追回 |
| payment.execute | 向未知账户支付 500,000 | 通常不可逆 |
| payment.execute | 十分钟内连续执行一百次 | 单次都在限内,累计后果不可逆 |
更值得追问的是为什么会这样。权限模型的切分依据,来自软件系统自身的结构:资源归属于哪个服务,接口挂在哪个命名空间下,哪些操作共享同一套数据模型,哪个团队负责维护这段代码。这是一套围绕资源所有权与接口边界形成的划分,它的设计目标是让权限可管理、可审计、可委派。
而现实风险的切分依据完全是另一套:这次动作是否可逆,恢复成本有多高,影响面有多大,是否触及资金、生产可用性或人身安全,出错之后由谁承担。这是一套围绕后果严重性形成的划分。
这两套划分标准出自不同的工程动机,它们在某些地方会重合,但没有任何机制保证它们对齐。一个在 API 设计上已经足够细的权限,可能在后果维度上仍然极粗;反过来,两个在后果上完全等价的操作,也可能因为分属两个服务而被拆成两个权限。把权限粒度当作风险粒度的代理指标,本质上是在用一套为了可管理性而设计的划分,去承担一个它没有被设计来承担的度量任务。
权限是软件能力的抽象,执行权是现实后果的抽象;两者会重合,但不能默认等价。
三、细粒度授权可以表达执行边界,但表达边界不等于约束能力
这里必须避免一个常见的不诚实论证。Least Execution Authority 并不建立在"传统 IAM 只能做粗粒度 RBAC"这个假设之上——那个假设已经过时很久了。成熟的 ABAC、PBAC、上下文感知授权与事务级策略完全可以表达金额、对象、时间窗口、地理位置、设备状态、风险等级、资源属性与业务状态。一条策略写成"Agent-A 只能向供应商组 X 执行支付,单笔不超过一万,当日累计不超过五万,且必须在工作时间内",在表达能力上已经非常接近一个执行边界。
所以真正的问题不是授权系统能不能表达这些条件,而是这些条件是否真正约束了最终执行能力。这需要继续追问:谁可以修改这条策略,谁可以往供应商组 X 里增加成员,谁决定最终到达执行器的 Target,谁持有真正调用支付系统的凭据,以及策略引擎返回 ALLOW 之后,执行侧是否还会校验最终参数与被批准的对象是否一致。
上一篇讨论对抗性完整时已经建立过这个结构:如果同一个 Authority 既能修改条件、又能构造参数、还持有最终凭据,那么策略写得再细,执行路径上仍然存在一个足以独立成事的单点权力。放到本篇的语境里,这个结论有一个更具体的形式——一个上限如果可以被受其约束的主体自己调高,它就不是执行边界,而是一条默认配置。
现实中确实存在配额、预算告警、速率限制这类机制,它们在形态上很接近执行边界。但它们通常部署在与授权模型不同的位置,服务于容量治理与成本控制,其失效语义也不同:超出配额往往触发限流或告警,而不是判定这次执行不具备资格。把它们直接当作安全边界使用之前,需要先确认它们在威胁模型中的独立性,以及它们被突破时系统的默认行为是什么。
一个边界只有在被它约束的主体无法单独将其扩大时,才真正构成执行边界。
四、最小权限是静态能力收缩,最小执行权更接近一种随状态消耗的预算
权限具有相当强的持续性。一个主体获得某个角色或作用域之后,在凭据有效期内会持续拥有对应能力。授权判断在语义上接近幂等:同样的主体、同样的接口、同样的上下文,第一次调用与第五十次调用得到的答案通常一致。这个性质对可缓存性与系统性能都有好处,也是权限模型能够大规模落地的原因之一。
现实执行的风险却不具备这个性质。同一个动作在不同的系统状态下,合理的执行范围可能完全不同。当日累计敞口是五千时,下一笔一万的交易可能仍在可接受范围内;当累计敞口已经到四万九千时,同样这笔一万就越界了。请求本身没有任何变化,主体、接口、参数、上下文全部一致,变的是这次执行被放进的那个状态。
这意味着 Least Execution Authority 在语义上更接近预算而不是权限位:它是可消耗的、可累计的,并且与系统当前状态耦合。它需要在每一次执行之前重新计算,而不是在身份创建或角色分配时确定一次:
当前主体 + 当前动作 + 当前对象 + 当前状态 + 当前边界 → 本次允许的最小执行空间
这个转变有明确的工程代价,必须诚实地写出来。可消耗的执行预算需要一个有状态的、被信任的计数与状态来源,而这个状态源一旦存在,它本身就成为一个 Authority:能够改写累计值的人,就能够扩大所有下游主体的执行范围。引入执行预算并不自动提高安全性,它把一部分风险从"权限过大"转移到了"状态源可信度"。这个转移是否划算,取决于被保护动作的不可逆程度——对于可回滚的操作,多半不划算;对于资金转移、集群删除、物理设备控制这类难以恢复的动作,通常划算。
Least Privilege 缩小一个主体长期持有的能力范围;Least Execution Authority 缩小它在当前状态下能够造成的后果范围。
五、最小执行权的表达单位是 Action、Target 与 Boundary,而不是 Permission
如果 Least Execution Authority 只是"权限再细一点",它就不值得单独命名。它需要一套自己的表达单位,至少覆盖三个维度。
Action 回答主体被允许执行什么动作。这里需要的是 Transfer 这样的具体动作,而不是 Financial Operations 这样的能力类别——类别是权限模型的自然产物,动作才是后果的载体。
Target 回答动作可以作用于哪些对象。这里需要的是一个可枚举、可校验、且其成员变更本身受控的对象集合,而不是"任意账户"。Target 的定义方式往往比 Action 更能决定实际风险,因为在多数误执行事故中,动作类型是对的,对象是错的。
Boundary 回答动作不能越过哪些现实边界:单笔上限、累计上限、时间窗口、频率、影响面(例如单次最多影响一个可用区)、以及在何种系统健康状态下必须停止。
三者组合起来,才形成一个比权限更贴近现实后果的能力描述。在此之上还可以引入 State 与 Evidence——即这次执行所依据的业务状态是否仍然有效、相关条件是否有可被验证的证明——于是一次执行资格的完整形态开始浮现:
Actor + Action + Target + State + Evidence + Boundary → Execution Authority
State 与 Evidence 如何被验证、以及"没有证据"应当被解释成什么,是本系列后面两个阶段要专门处理的问题,这里只需要确认它们属于执行资格的构成要件。就本篇而言,关键的对照是:
| 维度 | Least Privilege | Least Execution Authority |
|---|---|---|
| 表达单位 | Permission / Role / Scope | Action + Target + Boundary |
| 度量对象 | 持有的能力集合 | 可达的现实状态空间 |
| 判定时机 | 授予时确定,有效期内持续 | 每次执行前重新计算 |
| 语义性质 | 近似幂等 | 可消耗、可累计 |
| 收缩目标 | 移除不必要的能力 | 压缩必要能力的后果半径 |
六、最小执行权压缩的是错误的爆炸半径,不是自动化的频率
一个常见的反对意见是:不断收紧执行权,Agent 最终是不是只能做很少的事,自动化的价值也就被抵消了。
这个反对意见把目标搞反了。Least Execution Authority 的工程目的不是把自动执行降级回人工审批流程,恰恰相反,它是让高频自动执行变得可以被接受。任何一个现实系统都不能假设自己的执行主体永远不出错——模型会误解意图,编排会传错参数,上游数据会过期,外部接口会返回意外结果。既然出错是必然事件,那么更可行的工程目标就不是杜绝错误,而是允许错误发生,同时限定单次错误与累计错误能够造成的最大现实后果。
一个每天自动执行五百次支付的 Agent,安全目标未必是每一笔都人工确认——那样做的实际结果通常是审批被降格成机械点击,既没有提高安全性,也毁掉了自动化。更合理的目标是让它在预先定义的执行边界内自由运行,一旦金额、对象、频率、时间或系统状态越过边界,就停止执行或升级到更高一级的 Authority 处理。
这本质上是爆炸半径控制。最小权限已经很擅长控制数字资源层面的爆炸半径:凭据泄露后攻击者只能触及被授予的那几个接口。Least Execution Authority 进一步控制现实层面的爆炸半径:即使攻击者拿到了完全合法的 payment.execute,如果执行边界限定了单笔上限、当日累计上限,并且对象集合只能是预先注册且其成员变更由另一个 Authority 控制的账户,那么这份凭据所代表的最终现实能力已经被显著压缩。凭据的价值,从"可以调用支付"变成了"可以在一个有限区间内调用支付"。
Least Execution Authority 的价值不是让自动化变少,而是让错误执行的最大损失变小。
结语:从计量权限到计量后果
Least Execution Authority 不应该被理解为一个比 Least Privilege 更新、因而可以取代它的原则。两者处在一条连续的收缩链上,而且顺序不能颠倒。最小权限先解决能力入口的问题:Agent 不持有无关接口,服务账号不持有无关云角色,工作流不访问与任务无关的数据,凭据不携带不必要的作用域。这一步不做,后面所有关于执行边界的讨论都会失去意义——你没有办法为一个持有云管理员角色的主体设计有意义的执行边界,因为它可以从任何一个方向绕过去。
在此之上,Least Execution Authority 处理的是剩下那部分业务上确实必须拥有的能力:既然它必须能够部署,那它能部署什么镜像、作用于哪些服务、在什么时间窗口、单次最多影响多大范围、在系统健康状态异常时是否仍然允许执行。最小权限把主体从云管理员降到部署操作员,最小执行权再把部署操作员限定到一次具体的、有边界的现实动作上。两步叠加之后,安全边界才真正从数字权限延伸到现实后果。
这条链走下来,改变的其实是安全分析的计量单位。传统提问是"这个角色有几个权限、这个令牌有哪些作用域、这个身份能访问哪些资源";执行安全的提问变成"这个主体能影响哪些动作、作用于哪些对象、改变哪些状态、承受多大金额、以何种频率、持续多久、独立造成的最大现实后果是什么"。前者衡量 Access Capability,后者衡量 Execution Reach。
而当执行权被压缩到一个足够小的边界之后,一个新的问题会立刻浮上来:边界画好了,谁来证明当前这一次动作确实落在边界之内?主体身份为真、权限为真、边界存在,这三件事加起来仍然不等于"这一次执行有资格现在发生"。系统需要的不再只是一个身份证明,而是一次针对具体动作的资格证明。这就进入下一篇:Authentication vs Execution Qualification。
最小权限告诉我们,不要给一个主体超过任务所需的权限。最小执行权继续追问:即使这项能力它确实必须拥有,我们为什么要让它一次性拥有比这一次任务更大的现实执行范围。
更多推荐


所有评论(0)