企业出海多云非人类身份(NHI)治理实战:机器身份盘点、长期密钥清零与工作负载身份联邦(2026)

📅 · 企业出海多云架构实战 - ChengziCloud

一句话结论

Meta Description: 出海架构里人类身份有 SSO 与离职回收,机器身份却常裸奔。

本文要点
  • 前言:先把结论摆出来
  • 为什么 NHI 是出海架构里最后一个没上锁的门
  • 边界:NHI 治理与站内相邻主题的分工
  • NHI 全景:五类身份 × 四层信任
  • 第一步:盘点,把三朵云的机器身份拉成一张表
  • 第二步:风险量化,决定先动哪一把钥匙
  • 第三步:清零长期凭证,四条路径
  • 工作负载身份联邦:让机器不再需要那把密钥

Meta Description: 出海架构里人类身份有 SSO 与离职回收,机器身份却常裸奔。本文给出非人类身份(NHI)治理三步法:三云盘点、风险量化、长期密钥清零,附工作负载身份联邦与密钥蔓延防护实操。

关键词: 非人类身份、NHI 治理、机器身份、长期密钥清零、工作负载身份联邦

前言:先把结论摆出来

如果你的出海架构已经做了统一 SSO、MFA、离职当天回收权限,那么剩下最大的一个洞不在人身上,而在机器身份上:CI/CD 流水线里的长期 AccessKey、Kubernetes 里挂进环境变量的 AK/SK、监控 Agent 的 API Token、对接客户 ERP 的固定密钥、三年前某位离职同事创建的集成账号。

人类身份和机器身份在治理上的差别,不是"多一点少一点",而是四个维度全部相反:人类身份数量有限、有 HR 事件驱动、登录会留痕、失效靠离职;机器身份数量是人类的几十倍、没有入离职事件、调用是"合法"的所以不告警、失效只能靠人主动删。这就导致一个很典型的现象——安全团队能说清公司有多少员工,却说不清公司有多少把钥匙。

非人类身份(Non-Human Identity,NHI)治理的完整闭环只有三步,顺序不能颠倒:

  1. 盘清:把三朵云 + K8s + CI + SaaS 里的机器身份拉成一张统一清单,每条都必须有 owner;
  2. 收敛:按风险打分排序,把"永久有效、权限很大、没人认领"的先处理;
  3. 消灭长期凭证:用 OIDC 联邦、实例角色、工作负载身份把静态密钥换成几十分钟就过期的临时凭证。

本文按这三步给出可直接执行的三云命令、评分模型、IAM 信任策略、K8s 工作负载身份配置、密钥蔓延扫描流水线,以及合规评审时怎么举证。全文的前提是多云——单云场景下厂商自带工具能覆盖大半,多云场景下"清单归一"和"凭证最后使用时间"这两件事必须自己拼。

一、为什么 NHI 是出海架构里最后一个没上锁的门

1.1 三个几乎每家公司都有的场景

场景一:CI 里的"临时"密钥,用了三年。 2023 年为赶一个版本,运维在 GitHub Secrets 里放了一对 AccessKey,配置成部署用。后来流水线改成了别的方案,但这对密钥没删——它还在,权限是 AdministratorAccess,最近一次调用是 47 天前的一次手工排查。这类密钥的危险在于它不在任何人的日常视野里,但一旦仓库或 runner 被攻破,攻击者拿到的就是管理员权限,而且不需要任何 MFA。

场景二:K8s 里挂进环境变量的云凭证。 为了让 Pod 能读对象存储,早期做法是把 AK/SK 写进 Secret 再注入环境变量。问题有三层:Secret 是 base64 不是加密;任何能 exec 进 Pod 的人都能读到;这把密钥的权限通常是整个桶甚至整个账号,而不是"这个前缀只读"。

场景三:无人认领的集成账号。 对接物流商、支付网关、客服系统时,对方要求一个固定 API Key。这类凭证往往登记在某个人的笔记里,人走了、项目下线了,Key 还活着。它不属于任何服务,所以没有任何监控会盯它。

1.2 人类身份 vs 机器身份:四个维度全部相反

维度人类身份机器身份(NHI)治理含义
数量与增长与编制挂钩,可控随服务、环境、集成数量指数增长,通常是人类的数十倍靠人工登记必然漏,必须自动采集
生命周期驱动入职、转岗、离职等 HR 事件没有事件,只有"创建"这一个动作,没有"删除"动作必须人为设定到期时间与复核周期
可见性登录有日志、异常有告警调用是设计内的,行为合法,不触发任何告警只能靠"从未使用""异地使用"这类旁证发现异常
失效方式离职即停用,MFA 兜底无 MFA 可言,唯一的生命周期控制就是轮换与删除短寿命凭证是唯一有效的架构级解法

1.3 三条结论

  • NHI 不能靠"管人"的流程管。没有入离职事件,就必须给它装一个"到期时间"。
  • NHI 的泄漏不会有告警。攻击者用一把合法密钥调用 API,在日志里和正常业务没有区别,唯一的破口是"使用地/使用服务/使用时间"偏离基线。
  • NHI 的治理成本主要在盘点,不在技术。三朵云各有一套 API,字段口径完全不同,把清单归一这件事的工作量占整个项目的六成以上。

二、边界:NHI 治理与站内相邻主题的分工

这一节是为了避免和已有文章混读。站内已经写过统一身份认证、特权访问管理、KMS、PKI、供应链安全,它们各自解决一段,本文只解决"机器身份自身的数量、寿命、权限与归属"。

已有主题它回答的问题与本文的边界
多云统一身份认证(08-07)人怎么登录、怎么联邦、怎么同步组织架构只管人类身份;离职回收是 HR 事件驱动,NHI 没有这个事件
特权访问管理 PAM(09-29)人怎么临时提权、会话怎么录屏审计只管人类特权会话;本文管无人值守的机器凭证
密钥管理与 KMS(08-26)密钥怎么存、怎么加密、怎么轮换管"密钥的保管",本文管"密钥的数量与权限",且明确要求把长期密钥清零
PKI 与证书生命周期(10-13)证书怎么签发、怎么自动续期证书是 NHI 的一种载体,本文只做衔接,不重复签发流程
软件供应链安全(08-11)制品怎么签名、怎么验签管"产物可信",本文管"构建过程用的是什么身份"
零信任网络架构(08-25)访问凭什么被信任管访问决策,本文管被决策的那个主体本身

五个容易混的概念,一句话分清:

概念定义典型载体
人类身份有具体自然人对应的账号SSO 账号、IAM 用户、RAM 子账号
机器身份(NHI)由软件代表自己行使权限的身份服务账号、IAM 角色、API Key、证书、Token
服务账号一种被服务使用的账号实体,可能是长期凭证也可能是角色K8s ServiceAccount、数据库账号、SaaS 集成账号
工作负载身份把"身份"绑定到运行中的工作负载而非密钥文件EKS Pod Identity、IRSA、SPIFFE SVID
密钥(Secret)证明身份的一段字符串AccessKey、API Key、Token、私钥

一句话记法:人类身份靠"人"证明自己,机器身份靠"密钥"证明自己;工作负载身份的目的是让机器不再需要那把密钥。

三、NHI 全景:五类身份 × 四层信任

3.1 五类机器身份

类别举例主要风险优先治理手段
云平台长期凭证IAM 用户的 AccessKey、RAM AccessKey、CAM API 密钥永久有效、权限常过大、无法 MFA清零,改角色/联邦
云平台角色IAM Role、RAM 角色、CAM 角色信任策略过宽(* 主体)收紧信任策略条件
工作负载身份K8s ServiceAccount 绑定的云角色、SPIFFE SVID绑定粒度过粗(整个 namespace 共用一个角色)一工作负载一角色
CI/CD 与自动化令牌GitHub Actions Secrets、GitLab CI 变量、Jenkins 凭据存在仓库/runner 中,随仓库扩散OIDC 联邦,不落地
第三方集成凭证支付、物流、客服、监控 SaaS 的 API Key无主、无到期、无法轮换登记 + 设到期 + 网关代理

3.2 四层模型

NHI 治理不是一张清单,而是四层同时成立才算闭环:发现层(有没有全部找到)、收敛层(权限是不是最小)、寿命层(能不能自动过期)、证据层(能不能向审计举证)。


graph TD
    A[发现层:三云+ K8s + CI + SaaS 清单归一] --> B[收敛层:权限打分与最小化]
    B --> C[寿命层:临时凭证 / 自动轮换 / 到期删除]
    C --> D[证据层:清单快照 + 变更日志 + 复核记录]
    D --> A

┌──────────────────────────────────────────────────────────────────┐
│                        证 据 层(可举证)                          │
│   月度清单快照 │ 变更审计 │ 季度 owner 复核签署 │ 例外台账          │
├──────────────────────────────────────────────────────────────────┤
│                        寿 命 层(会过期)                          │
│   OIDC 联邦(≤1h) │ 实例角色 │ 工作负载身份 │ Secrets 轮换 │ 到期删除 │
├──────────────────────────────────────────────────────────────────┤
│                        收 敛 层(最小权限)                        │
│   权限边界 │ 条件键 │ 会话标签 │ 一工作负载一角色 │ 只读优先        │
├──────────────────────────────────────────────────────────────────┤
│                        发 现 层(全都在册)                        │
│  AWS 凭据报告 │ 阿里云 RAM │ 腾讯云 CAM │ K8s SA │ CI Secrets │ SaaS │
└──────────────────────────────────────────────────────────────────┘
          ▲ 单向依赖:上面每一层都依赖下一层的准确性

三条铁律:

  • 发现层不准,上面三层全是自欺。清单漏了 20%,剩下的治理动作只覆盖了 80% 的风险敞口,而攻击者只会挑那 20%。
  • 没有 owner 的机器身份,等于没有治理。owner 不是"某个人名",必须是能收到告警、能审批轮换的团队。
  • 寿命层的收益最大、成本最低。权限收敛需要理解业务,寿命收敛只需要改架构,而短寿命凭证把"泄漏后的窗口"从永久压缩到一小时。

四、第一步:盘点,把三朵云的机器身份拉成一张表

4.1 统一清单的 12 个字段

不管来自哪朵云,最终都要落成同一张表。字段少一个,后面的评分就会失真:

字段说明缺失后果
id云平台内的唯一标识无法回溯
cloudaws / aliyun / tencent无法分组统计
typeuser / role / sa / api_key / cert / token无法匹配治理手段
name名称人工核对困难
arn_or_id完整资源名无法自动处置
owner负责团队无人认领
created_at创建时间无法算寿命
last_used_at最后使用时间无法判断僵尸
expires_at到期时间(若有)无法自动清理
permissions权限摘要(策略/角色)无法算风险分
exposure是否可公网使用 / 是否在代码库中无法定优先级
statusactive / disabled / pending_delete无法统计进度

其中最难拿到的两个字段是 last_used_at 和 exposure,也是决定优先级的关键。

4.2 AWS:凭据报告是唯一全量入口

AWS 的 IAM 用户长期凭证,靠 generate-credential-report 一次拉全。这份报告是 CSV,以 base64 返回,包含每个 IAM 用户的密码状态、MFA 状态、两把 AccessKey 的创建时间、最后使用时间、最后使用区域与最后使用的服务。


aws iam generate-credential-report
aws iam get-credential-report --query Content --output text | base64 -d > /tmp/cred_report.csv
aws iam list-users --query "Users[].UserName" --output text
aws iam get-access-key-last-used --access-key-id AKIAXXXXXXXXXXXXXXXX

解析出三个关键列就能做第一轮筛选:access_key_1_active、access_key_1_last_used_date、access_key_1_last_rotated。空的使用日期 = 从未使用,这类密钥应当直接进入删除队列。

两个坑:

  • 凭据报告有刷新频率限制,AWS 侧大约每 4 小时才重新生成一次,短时间内重复调用拿到的还是旧数据。做自动化时不要按"每次运行都生成"来设计,改成读缓存 + 每日一次。
  • 凭据报告只覆盖 IAM 用户,不覆盖角色。角色没有"永久密钥",但角色有"信任策略",那才是角色侧的风险点,需要单独用 list-roles + 解析 AssumeRolePolicyDocument 来做。

4.3 阿里云 RAM:AccessKey 与最后使用时间


aliyun ram ListUsers --MaxItems 100
aliyun ram ListAccessKeys --UserName svc-deploy --output cols=AccessKeyId,Status,CreateDate
aliyun ram GetAccessKeyLastUsed --UserAccessKeyId LTAI5tXXXXXXXXXXXX
aliyun ram ListPoliciesForUser --UserName svc-deploy

阿里云 RAM 的 GetAccessKeyLastUsed 返回的是"最后一次使用时间 + 最后使用的服务",字段比 AWS 简短,但足够判断僵尸密钥。注意分页:ListUsers 默认只返回 100 条,账号多的时候必须循环 Marker。

4.4 腾讯云 CAM:清单有了,最后使用时间要靠审计日志


tccli cam ListUsers
tccli cam ListAccessKeys --TargetUin 100000000001
tccli cloudaudit LookupEvents --LookupAttributes.0.AttributeKey AccessKeyId --LookupAttributes.0.AttributeValue AKIDxxxxxxxxxxxx --StartTime 2026-07-01T00:00:00Z --EndTime 2026-10-22T00:00:00Z

腾讯云 CAM 的 ListAccessKeys 返回密钥 ID、状态与创建时间,但最后使用时间不在这个接口里——这一点与 AWS、阿里云不同。要用云审计 LookupEvents 按 AccessKeyId 反查,把"有调用记录"当作活跃证据,查不到就归为疑似僵尸。这也是多云盘点最容易被低估的工作量:三家云的"最后使用时间"语义和取法都不一样,归一逻辑必须自己写。

4.5 K8s 与 CI 侧

工作负载身份不需要"凭据报告",但它需要两个额外采集:

  • 集群侧:列出所有 ServiceAccount,以及绑定到 SA 的云角色注解。没有绑定角色的 SA 是安全的,绑了但整个 namespace 共用一个角色的才是问题。
  • CI 侧:把仓库变量/Secrets 的名称(不是值)导出成清单,并按名称匹配"疑似长期云凭证"(如 AWS_ACCESS_KEY_ID、ALIYUN_ACCESS_KEY)。OIDC 方案落地的团队,这类变量应当为零。

五、第二步:风险量化,决定先动哪一把钥匙

清单拉出来之后,几千条记录不能靠感觉排序。用五维打分,每一项 1~5 分,加权后落到 P0~P3:

维度权重5 分(最危险)1 分(最安全)
权限广度×2管理员/:单一资源只读
凭证寿命×2永久有效且从未轮换≤1 小时自动过期
暴露面×3出现在代码库/公网可达/挂进环境变量仅内网、由角色承载
活跃度×1长期静默但权限大(僵尸)持续使用且有基线
归属清晰度×2无 owner 或 owner 已离职有团队且季度复核

总分 = 权限×2 + 寿命×2 + 暴露×3 + 活跃×1 + 归属×2,满分 50。

分数区间等级处置时限处置动作
≥40P072 小时内立即禁用 → 观察 → 删除;若在用则改为角色并轮换
30~39P12 周收权限到最小集 + 设到期 + 补 owner
20~29P21 个季度纳入季度复核,逐步替换为临时凭证
<20P3半年保持监控,随架构演进自然淘汰

为什么"暴露面"权重最高(×3):权限大但只在内网、由角色承载的凭证,攻击者要先进入网络才能用;而写在代码库里的密钥,任何拿到仓库读权限的人都能用,且Git 历史会永久保留它。同一个权限等级下,后者危险一个数量级。

为什么"活跃度"权重最低(×1):直觉上"没在用"最该删,但打分时它只是加分项而不是主项。因为一把天天在用的管理员密钥,比一把闲置的只读密钥危险得多——前者一旦泄漏立刻能用,后者泄漏了也做不了什么。先按"权限 × 暴露"排序,再用"活跃度"做同分时的裁决。

六、第三步:清零长期凭证,四条路径

"清零"不是把所有 AccessKey 删掉(那会把业务停掉),而是把每一条长期凭证替换成一条不需要长期凭证的路径。四条路径的适用面完全不同:

路径适用场景凭证寿命迁移成本备注
OIDC 联邦CI/CD、外部系统调用云 API分钟级低主流 CI 均已支持,首选
实例/节点角色跑在云主机上的服务小时级,自动轮换低消除"配置文件里的 AK"
工作负载身份K8s Pod、容器化服务分钟级中需集群支持与 RBAC 配合
短期令牌换取遗留系统(不支持角色)小时级中用一处长期凭证换多处短期凭证,收敛为一个高风险点

迁移顺序(很重要的经验):

  1. 先做实例/工作负载角色。这类迁移是"删掉环境变量 + 加一个角色绑定",业务代码几乎不用改,SDK 会自动从元数据服务取临时凭证。
  2. 再做CI 的 OIDC 联邦。这一步要改流水线配置和云侧信任策略,但收益最大——CI 是最容易泄漏长期密钥的地方。
  3. 最后处理遗留系统。它们往往没有 SDK、只能读固定配置文件,用"令牌代理"把长期凭证收敛到一处,再对这一处加网络限制与使用监控。

兜底策略:禁用优先于删除。

删除是不可逆的,而"这把密钥是否真的没人用"很难 100% 确认。正确做法是先禁用,观察 14 天:

  • 禁用期间业务正常 → 删除;
  • 禁用期间出现调用失败 → 立刻启用,说明还有隐藏调用方,改为"收权限 + 定期轮换"而不是删除。

这条规则的价值不在技术,而在组织——它让"删密钥"从一件需要开会讨论的事,变成一件可以先动手的事。团队愿意执行,治理才推得动。

七、工作负载身份联邦:让机器不再需要那把密钥

7.1 原理:从"证明我知道密钥"到"证明我是谁"

静态凭证的逻辑是"谁持有字符串谁就有权限",所以字符串一旦泄漏,身份就被复制了。联邦的逻辑是"由平台证明这个工作负载的身份",云侧只认平台的签名,没有任何可被复制的长期字符串。

在 AWS 上有两条路:OIDC 联邦(外部系统,如 GitHub Actions)和 EKS Pod Identity(集群内的 Pod)。

7.2 GitHub Actions 的 OIDC 信任策略

关键是信任策略里的三个条件:sub 限定到具体仓库与分支、aud 限定为 STS、以及不要用 "Principal": "*" 裸放。


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Federated": "arn:aws:iam::111122223333:oidc-provider/token.actions.githubusercontent.com" },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:ref:refs/heads/main"
        }
      }
    }
  ]
}

三个常见错误:

  • sub 写成 repo:your-org/*,等于组织内任何仓库的任何分支都能AssumeRole——这和一个共享账号没区别;
  • 忘记加 aud 条件,其他平台的 token 可能被复用;
  • 把 refs/heads/main 换成 pull_request 事件却没意识到 PR 来自 fork 时可以触发。

7.3 EKS Pod Identity:三步取代 IRSA

EKS Pod Identity 的核心是把"角色 ↔ 命名空间 ↔ ServiceAccount"的映射做成集群内的一等资源,不再需要给集群配 OIDC Provider,这也是它比 IRSA 简单的根本原因。AWS 官方文档明确写了这一点,同时也写明一件常被忽略的事:容器不是安全边界,Pod Identity 不改变这个事实——同一个节点上的 Pod 共享内核,所以身份隔离必须配合网络策略与调度隔离。


aws eks create-pod-identity-association \
  --cluster-name prod-sg \
  --namespace payments \
  --service-account payments-api \
  --role-arn arn:aws:iam::111122223333:role/payments-s3-readonly

配套的 ServiceAccount 只需要普通声明,不打角色注解:


apiVersion: v1
kind: ServiceAccount
metadata:
  name: payments-api
  namespace: payments

Pod Identity 与 IRSA 的选型:

维度EKS Pod IdentityIRSA
是否需集群 OIDC Provider不需要需要
角色信任策略主体pods.eks.amazonaws.com集群 OIDC Provider
跨集群复用同一角色支持(关联多个集群)需为每个集群的 OIDC 分别授权
迁移成本新建关联即可,可与 IRSA 并存存量方案,注解已写死在清单里
建议新集群一律用 Pod Identity存量集群逐步迁移,不必一次性切换

7.4 阿里云与腾讯云的对应能力

  • 阿里云 ACK:RRSA(RAM Roles for Service Accounts),基于集群 OIDC 颁发方 + RAM 角色的信任策略,Pod 通过 ServiceAccount 绑定 RAM 角色拿到 STS 临时凭证。落地三步是:开启集群 RRSA → 创建 RAM 角色并把信任策略限定到该集群的 OIDC 主体与 namespace/SA → 给业务 ServiceAccount 绑定角色。
  • 腾讯云 TKE:通过集群的服务账号与 CAM 角色绑定,让 Pod 以角色身份调用云 API,同样避免在容器里落长期密钥。注意腾讯云的 CAM 角色信任策略语法与 AWS 差异较大,迁移时不能照抄 JSON。

两家共同的一点:绑定粒度决定安全水位。一个 namespace 一个角色是最省事也最危险的做法,正确做法是一个工作负载一个角色,权限只覆盖它真正需要的那几个资源。

7.5 三朵云工作负载身份能力对比

能力AWS阿里云腾讯云
集群内工作负载身份EKS Pod Identity / IRSAACK RRSATKE 服务账号绑定 CAM 角色
是否依赖 OIDC ProviderPod Identity 不依赖依赖集群 OIDC依赖集群 OIDC
临时凭证有效期由角色会话控制由 STS 控制由 STS 控制
外部系统联邦IAM OIDC / SAMLRAM SSO / OIDCCAM SSO / OIDC
权限边界机制Permissions Boundary角色权限策略 + 管控策略权限边界(策略限制)
条件键丰富度最高(含 VPC/OrgID/源 IP)较高中等

八、SPIFFE/SPIRE:跨云统一工作负载身份

8.1 什么时候需要它

工作负载身份在单朵云里已经够用(Pod Identity、RRSA、TKE 服务账号)。但出海架构常见的形态是"两朵云 + 自建 IDC",这时候会出现一个尴尬局面:每个集群有自己的 OIDC Provider,每个云有自己的信任策略,一个服务从 AWS 迁到阿里云,就要重写一遍身份配置。

SPIFFE 解决的正是这件事:它定义一个与云无关的工作负载身份格式(SVID,本质是一张短寿命 X.509 证书或 JWT),由 SPIRE Server 统一签发,各云的信任策略只需要信任同一个 trust domain。

判断要不要上 SPIFFE,看三条:

判断条件上 SPIFFE不上
工作负载是否跨两朵以上云、且需要互相认证是否
是否有自建 IDC / 边缘节点参与服务间调用是否
团队是否已有统一 CA 与证书运维能力是否(先做 PKI 再做身份)

第三条经常被忽略:SPIFFE 的底座是证书签发与轮换,如果团队还没把证书生命周期管起来(见 10-13 的 PKI 篇),上 SPIFFE 只会把"证书过期"这个故障模式复制到每一个服务上。

8.2 与 PKI 篇的边界

  • PKI 篇解决"证书从哪来、怎么自动续、怎么盘点"——它是机制;
  • 本文解决"哪个工作负载该拿到什么身份、这个身份对应什么权限"——它是主体与授权的映射。

SPIFFE 把两者接起来:SVID 是证书(机制来自 PKI),它的 trust domain 与 SPIFFE ID 是身份(语义来自本文)。

8.3 落地骨架

SPIRE 的部署形态是 Server(签发 + 注册表)+ Agent(每个节点一个,向工作负载发放 SVID)。关键配置只有两处:


apiVersion: v1
kind: ConfigMap
metadata:
  name: spire-server-config
  namespace: spire
data:
  server.conf: |
    trust_domain = "corp.example.com"
    bind_address = "0.0.0.0"
    bind_port = "8081"

注册条目(registration entry)把"选择器"映射到"SPIFFE ID",这是整个体系的权限边界所在:


spire-server entry create \
  -parentID spiffe://corp.example.com/node/sg-01 \
  -spiffeID spiffe://corp.example.com/payments/api \
  -selector k8s:ns:payments \
  -selector k8s:sa:payments-api

三条工程纪律:

  • 选择器必须同时限定 namespace 与 serviceaccount,只写 namespace 等于该命名空间内所有 Pod 共享身份;
  • trust domain 一旦确定不要改,改了所有下游信任策略都要跟着改;
  • SVID 寿命设短(小时级),把"泄漏窗口"压到与业务容忍度一致,续期由 Agent 自动完成。

九、密钥蔓延治理:从"事后响应"变成"入口拦截"

9.1 五类泄漏源

泄漏源典型形态特点拦截点
代码库硬编码源码里的 AKIA...、配置文件里的 password随 Git 历史永久存在pre-commit + CI 扫描
CI 变量外泄流水线日志打印环境变量、fork PR 可读影响面覆盖整个仓库日志脱敏 + 权限收紧
镜像层残留Dockerfile 里 COPY .env、构建缓存镜像公开即泄漏构建期扫描 + 多阶段构建
配置文件分发运维把 AK 写进 Ansible/Jenkins 配置扩散到多台机器改为角色 + 集中配置中心
第三方泄露SaaS 平台被攻破、供应商日志外泄不受自己控制缩短寿命 + 使用监控

9.2 扫描工具对比

工具优势局限适用位置
gitleaks规则成熟、速度快、退出码友好只做正则与熵检测,误报需调pre-commit + CI
trufflehog支持验证凭证是否仍然有效验证需出网,可能触发风控CI 定时全仓扫描
detect-secrets基线机制适合存量仓库需维护 baseline 文件存量项目接入
云厂商原生与平台告警联动通常只覆盖本云凭证运行时兜底

推荐组合:gitleaks 做入口拦截(提交时),trufflehog 做周期性验证扫描(每周全仓 + 全历史),云厂商原生能力做运行时兜底。三者不是替代关系——入口挡住新泄漏,周期扫描找存量,运行时兜住"已经泄漏但还在用"的情况。

9.3 入口拦截流水线


gitleaks protect --staged --redact --verbose
gitleaks detect --source . --report-format json --report-path /tmp/leaks.json

三个工程细节:

  • --redact 必开。不加这个参数,CI 日志会把命中的密钥明文打印出来——扫描工具本身成了泄漏渠道。
  • pre-commit 只挡新增。protect --staged 只扫描本次暂存内容,存量历史必须靠 detect 全量扫。
  • 退出码要真的阻断。很多团队把扫描放进 CI 但设成 continue-on-error,等于没有。

9.4 已经泄漏了怎么办:顺序不能错

正确顺序是"先轮换,再取证,最后清历史"。 反过来做会出事:

  1. 轮换/禁用凭证——这是唯一能立刻止损的动作,其他都是善后;
  2. 取证——用云审计日志查这把凭证在泄漏窗口内做了什么(调用了哪些 API、从哪些 IP、有没有创建新用户或新密钥);
  3. 清理 Git 历史——用 git filter-repo 重写历史并强推,同时通知所有协作者重新克隆;
  4. 补流程——把这次泄漏的路径写进 pre-commit 规则或 CI 门禁。

git filter-repo 是重写历史的操作,会改变所有 commit 哈希,必须在团队约定的时间窗内做,并提前通知。而且要说清一件事:清历史不能替代轮换。已经被克隆、被 fork、被 CI 缓存过的仓库,历史永远清不干净。

十、运行时防滥用:让"合法凭证"也做不了大事

短期凭证解决了"寿命",但没有解决"权限"。一个权限过大的角色,即使只有一小时有效期,被利用的后果依然严重。运行时这一层做三件事:缩小单次可做的事、限制能从哪里做、发现异常的做法。

10.1 权限边界:给 NHI 装一个天花板

AWS 的 Permissions Boundary 是专门为"委托创建角色"设计的:它规定"这个角色最多能有什么权限",即使附加了管理员策略,实际权限也不会超过边界。


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket"],
      "Resource": [
        "arn:aws:s3:::payments-export-sg",
        "arn:aws:s3:::payments-export-sg/*"
      ]
    },
    {
      "Effect": "Deny",
      "Action": ["s3:DeleteBucket", "s3:PutBucketPolicy"],
      "Resource": "*"
    }
  ]
}

边界里放 Deny 比放 Allow 更重要:显式 Deny 是唯一能压住"上层策略写错"的机制。无论业务角色后面被谁加了多少权限,这几条 Deny 都生效。

10.2 条件键:把凭证钉在它该在的地方

条件键作用典型用法
aws:SourceVpce限定只能通过指定 VPC 端点访问对象存储只允许私网端点访问
aws:SourceVpc限定来源 VPC内部服务之间的调用
aws:PrincipalOrgID限定只能被本组织账号使用防止资源被外部账号引用
aws:RequestedRegion限定可用区域数据驻留合规,防止误调其他区域
aws:PrincipalTag/*按会话标签授权一个角色服务多个租户时做隔离

条件键是"低成本高收益"的典型:改一条策略,就能把"密钥泄漏后可全球使用"变成"密钥泄漏后只能从公司 VPC 内使用"。对 CI 场景尤其有效——CI 的出口 IP 或 VPC 通常是固定的。

10.3 会话标签与角色链

当多个服务共用一个角色时(不推荐但常见),用 sts:TagSession 在 AssumeRole 时打标签,再在权限策略里用 aws:PrincipalTag 做区分。这样即使角色相同,不同服务拿到的会话权限也不同,审计日志里也能看出"这次调用是哪个服务发起的"。

10.4 异常检测:三类信号

机器身份的异常不体现在"失败",而体现在"偏离基线"。三类信号最值得配告警:

信号具体表现数据源
地理位置异常凭证突然从非业务区域调用CloudTrail 的 sourceIPAddress + 地域基线
服务组合异常一个只读服务突然调用 IAM、STS、EC2 相关 APICloudTrail 的 eventName 白名单
凭证外带特征出现 GetCallerIdentity 高频探测、批量 CreateAccessKeyGuardDuty 相关 finding

GuardDuty 里与 NHI 直接相关的几类 finding 值得单独看:UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS(实例凭证在云外被使用,最典型的"AK 被打包带走")、CredentialAccess:IAMUser/AnomalousBehavior(凭证使用行为异常)、Recon:IAMUser/MaliciousIPCaller(来自恶意 IP 的侦察)。这三类 finding 的共同点是——它们描述的都是"合法凭证的非法使用",这正是 NHI 风险的核心形态。

十一、三朵云 NHI 治理能力对比

能力AWS阿里云腾讯云
长期凭证全量清单凭据报告(CSV)RAM ListAccessKeysCAM ListAccessKeys
最后使用时间凭据报告内置GetAccessKeyLastUsed需查云审计
未使用凭证分析Access Analyzer(unused access)安全中心 / 配置审计规则云审计 + 自定义规则
权限最小化建议Access Analyzer 策略生成RAM 权限诊断CAM 访问分析
外部系统联邦IAM OIDC / SAMLRAM SSO / OIDCCAM SSO / OIDC
工作负载身份Pod Identity / IRSAACK RRSATKE 服务账号
权限边界Permissions Boundary角色策略 + 管控策略权限边界
条件键丰富度最高较高中等
凭证滥用检测GuardDuty云安全中心主机/云审计告警

三条选型结论:

  • AWS 的工具链最完整:清单、最后使用、未使用分析、策略生成四件事都在原生服务里,可以直接搭自动化;
  • 阿里云的关键能力齐全但分散:清单与最后使用在 RAM,未使用分析在安全中心或配置审计,需要自己拼;
  • 腾讯云缺"最后使用时间"这个原生字段,必须靠云审计反查——这意味着盘点脚本要为它单独写一段逻辑,不能指望三家同一套代码。

十二、合规映射:审计会问哪几条

NHI 治理之所以必须做,很大一部分动力来自审计。四个常见框架里,与机器身份直接相关的条款如下:

框架条款方向审计实际要什么
SOC 2CC6.1 / CC6.2 / CC6.3(逻辑访问)谁能访问、权限如何授予与撤销、是否定期复核
ISO 27001A.5.15 / A.5.16 / A.5.17 / A.8.2(访问控制与特权访问)访问控制策略、身份生命周期、特权访问限制
等保三级身份鉴别、访问控制、安全审计是否有唯一标识、是否最小权限、操作是否可追溯
PCI DSS 4.08.6(应用与系统账号管理)系统账号是否受管、是否禁止交互式登录、密钥是否定期更换

审计真正想要的不是"我们做了治理",而是"我们能证明"。 三样材料最有效:

  1. 月度清单快照:证明"我们知道有多少机器身份",且数量在下降;
  2. 变更记录:证明"每一次创建、轮换、删除都有记录",而不是年底补材料;
  3. 季度 owner 复核签署:证明"每条身份都有人认领",这是审计最常挑的毛病——清单里没有 owner 的条目,审计会直接判定为失控。

十三、成本与组织:钱不是主要成本

13.1 直接成本

成本项口径量级
联邦与临时凭证STS / 角色调用基本为零,按请求计费可忽略
未使用凭证分析Access Analyzer 类能力AWS 侧该类分析不额外收费
密钥托管KMS 密钥约 1 美元/密钥/月
Secrets 存储Secrets Manager 类服务约 0.4 美元/密钥/月 + 调用费
扫描工具gitleaks / trufflehog开源免费
自建 SPIRE2~3 台小规格实例每月几十美元量级

价格为公开定价的量级参考,具体以各厂商官网实时报价为准;不同区域、不同计费方式会有差异。

13.2 真实成本是人力

三条经验:

  • 盘点占 60% 工作量。三家的字段口径不同、分页不同、最后使用时间的取法不同,这部分不可能靠买工具省掉。
  • 迁移占 30%。真正改代码的地方不多,主要是改流水线、改清单、改信任策略,以及一轮轮沟通"这把密钥到底谁在用"。
  • 持续运营占 10%,但它是唯一不能停的部分。清单会腐化——新服务上线、新集成接入,如果不做"上线即登记",三个月后清单又是不准的。

13.3 组织上的三条建议

  • NHI 登记写进上线流程,和"申请域名""申请数据库"同级。不上线登记就拿不到权限,这是唯一能长期保鲜的机制。
  • owner 必须是团队而不是个人。个人会离职、会转岗,团队才有告警接收人和审批能力。
  • 每季度做一次"僵尸清理日",固定时间、固定范围(全部 P0/P1 + 抽样 P2),产出物是清单快照与处置记录。固定节奏比运动式治理有效得多。

十四、90 天落地路线

阶段时间交付物验收标准
一、可见第 1~3 周三云 + K8s + CI 统一清单(12 字段)能回答"我们有多少把钥匙",且每条有 owner 字段
二、收敛第 4~7 周五维评分 + P0/P1 处置完成P0 清零,P1 全部收到最小权限或设到期
三、消灭第 8~11 周CI 全量 OIDC、工作负载身份覆盖核心服务新增长期凭证为零;核心服务不再持有静态 AK
四、固化第 12~13 周上线登记流程 + 季度复核机制 + 扫描门禁门禁能真的阻断;季度复核有签署记录

15 分钟验收四问(能连续答上来才算做完):

  1. 我们一共有多少个机器身份?分布在哪些云、哪些集群、哪些 CI?
  2. 有多少条是"永久有效 + 管理员权限"?它们分别属于哪个团队?
  3. 最近 30 天新增了几条长期凭证?为什么不是零?
  4. 如果明天发现一把 AK 泄漏,我们能在多长时间内定位它做过什么、影响哪些数据?

总结

  • 人类身份的治理靠流程(入职、离职、MFA),机器身份没有这些事件,只能靠"到期时间"和"owner"这两个人为装置。
  • NHI 治理的顺序是盘清 → 收敛 → 消灭长期凭证,顺序颠倒会做出"看起来很安全但漏了 20%"的假象。
  • 三步里最值钱的一步是清零长期凭证:它把"泄漏后的窗口"从永久压缩到一小时,而成本只是改流水线和加角色绑定。
  • 工作负载身份(Pod Identity / RRSA / SPIFFE)的本质不是"更安全的密钥",而是让密钥这件事不存在。
  • 治理能不能长期有效,取决于一条流程:上线即登记。没有这条,清单三个月就会腐化。

🚀 企业出海需要云架构咨询?通过 7.chengzicloud.cloud 联系我们,获取专属方案。覆盖多云身份治理、密钥与凭证体系、合规举证材料准备。

相关阅读

本文由 7.chengzicloud.cloud 提供,点击访问首页了解更多

常见问题(FAQ)

Q1:NHI 治理和 IAM 治理到底有什么区别?

IAM 治理的对象是"权限",问的是"谁能做什么";NHI 治理的对象是"身份实体",问的是"我们一共有多少个身份、它们各自活多久、谁负责"。前者是策略层面的收敛,后者是资产层面的盘点与生命周期管理。实践中两者共用一套策略工具,但盘点口径与 owner 机制是 NHI 独有的——IAM 治理通常不会要求"每一个角色都必须有团队认领并季度复核"。

Q2:小团队最少要做哪三件事?

只做三件:① 拉一次三云凭据清单,把所有"从未使用"和"超过 180 天未轮换"的 AccessKey 禁用;② 把 CI 改成 OIDC 联邦,删掉流水线里的长期密钥;③ 给所有机器身份补 owner 字段。这三件事覆盖了绝大多数真实事故的成因,成本是几天人力,不需要买任何工具。

Q3:长期 AccessKey 一定要清零吗?遗留系统不支持角色怎么办?

"清零"的目标是把长期凭证的暴露面压到最小,不是教条地要求零。对确实不支持角色的遗留系统,正确做法是"收敛":把它需要的那把长期凭证集中到一处(一台跳板机、一个令牌代理服务),对这一处加网络限制、加使用监控、设固定轮换周期,并且只保留这一把。真正要禁止的是"同一权限等级的长期密钥散落在十几台机器和几个仓库里"。

Q4:EKS Pod Identity 和 IRSA 怎么选?

新集群直接用 Pod Identity,因为它不需要给集群配 OIDC Provider、跨集群可以复用同一个角色、绑定关系是集群内的一等资源。存量集群不必一次性切换——两者可以并存,按命名空间逐步迁移。判断迁移优先级看两点:这个 SA 绑定的角色权限有多大、这个服务是否跨集群部署。权限大又跨集群的,先迁。

Q5:SPIFFE 是不是必须上?

不是。单云或双云但工作负载不互相认证的场景,各云原生的工作负载身份就够了。SPIFFE 的价值出现在"跨云 + 自建 IDC 的服务间互认",以及"需要一个与云无关的身份标识来做授权"的时候。而且它的前置条件是团队已经具备证书签发与轮换能力——没有这个底座,先做 PKI。

Q6:发现密钥泄漏,第一步做什么?

立刻轮换或禁用,不要先查日志。 取证很重要,但泄漏窗口每多一分钟,攻击者能做的事就多一分。正确顺序是:禁用 → 用云审计查这把凭证在窗口内的全部调用 → 检查是否被用来创建了新的用户/密钥/角色(这是最容易被忽略的持久化手段)→ 清理 Git 历史 → 补门禁。特别注意第二和第三步之间要检查"横向扩散",攻击者拿到一把密钥后的第一件事往往是建一个自己的后门账号。

Q7:三朵云能不能用一套脚本统一盘点?

能,但不要指望"一套代码三家用"。三家在三个地方不一样:字段命名(last_used_date vs 最后使用时间 vs 需查审计)、分页机制、以及权限模型(AWS 的 user/role 分离,阿里云与腾讯云的子账号体系不同)。可行做法是"统一输出 schema + 三个适配器",输出层共用,采集层各写一份。这样清单口径一致,采集逻辑可以独立演进。

Q8:怎么向审计证明"我们控制了机器身份"?

三样东西:① 有日期戳的清单快照(月度,能看到数量趋势);② 每一次创建/轮换/删除的变更记录(来自云审计,不是 Excel);③ 季度复核的签署记录(每条身份有 owner、有复核结论)。审计不看你用了什么工具,看的是这三样能不能对上——清单里的条目、变更记录里的事件、复核记录里的签署,三者数量与时间要能互相印证。