安全与供应链:密钥 / OIDC / 供应链加固
基于 CI/CD 通用模型 · 核于 2026-07
速查
- 密钥管理:存平台加密 Secret → 运行时以环境变量/文件注入 → 日志自动打码(masking);绝不明文写进仓库 YAML / 打进镜像层 / 打印到日志。
- OIDC 无密钥认证:CI 在运行时出示短时身份令牌,云厂商据此换临时凭据——仓库无需存长期访问密钥,消除长期密钥泄露风险,可按仓库/分支/环境细粒度授信。
- fork PR 默认不给密钥:来自不受信任外部贡献者的代码,若自动暴露密钥,攻击者一个 PR 就能窃取——所以默认不注入 secrets。
- 临时 Runner(ephemeral):每个 Job 用全新环境、跑完销毁,避免上一个 Job 的残留(凭据/后门)污染下一个,防横向渗透。
- 自托管 Runner 风险:能访问内网、可定制,但对公开仓库跑 fork 代码有重大风险,需严格隔离或避免。
- 第三方 Action 用 SHA 固定:
@v3/@main等可变标签可能被上游篡改(tag 可移动);用完整提交 SHA pin 确保引用的是审查过的代码。 - SLSA / provenance:provenance 是可验证的元数据,记录「制品由哪条流水线、哪个提交、什么过程产出」,用于验证来源可信、未被篡改。
- 缓存投毒(cache poisoning):不可信运行写入的缓存被可信构建复用会污染产物——按可信度隔离缓存作用域、fork 只读、键绑内容哈希。
- 最小权限:给流水线令牌/凭据最小必要权限(如只读 vs 可发布),限制爆炸半径。
一、密钥(secrets)的正确姿势
密钥(API key、部署凭据、签名证书)是 CI/CD 安全的重灾区。原则:
- 加密存储:放平台的加密 Secret 存储(GitHub Actions secrets、GitLab CI/CD variables 等),不入代码库。
- 运行时注入:以环境变量或临时文件注入到需要的 Job;用完即弃。
- 日志打码:平台会对已知 secret 值做 masking;但仍要避免主动
echo密钥、或让它出现在错误堆栈里。 - 最小权限 + 范围限定:如 GitLab 的「受保护变量」只注入受保护分支的流水线,避免普通/ fork 分支读到生产密钥。
绝对禁止:把密钥明文写进仓库 YAML、打进 Docker 镜像层(可被 dump)、打印到可能公开的日志。
二、OIDC:迈向「无密钥(keyless)」云部署
长期云访问密钥(AWS access key 等)存在仓库里,是最大的长期泄露风险源。现代做法用 OIDC(OpenID Connect):
- CI 平台在 Job 运行时签发一个短时有效的身份令牌(ID token),携带「我是哪个仓库/分支/环境的流水线」等声明。
- 云厂商(AWS/GCP/Azure)预先配置好「信任这个 CI、这些声明可换某角色」;Job 用该令牌换取短时临时凭据去操作云资源。
- 结果:仓库里不再存放任何长期云密钥;令牌短时、可按仓库/分支/环境精细授信;泄露风险大幅下降。
这是 2026 年安全部署到云的主流方案,GitHub Actions、GitLab CI/CD 都支持。
三、不受信任代码:fork PR 的隔离
对公开仓库,来自 fork 的 Pull Request 的代码来自不受信任的外部贡献者:
- 若自动把仓库密钥暴露给它运行,攻击者只需提一个「打印/外传密钥」的 PR 就能窃取。
- 因此平台默认对 fork PR 不注入 secrets(或限制权限)。像 GitHub 的
pull_request_target(在基仓上下文运行、能拿到密钥)要格外小心,别在其中 checkout 并执行不可信代码。
四、Runner 的安全:临时化与隔离
- 临时(ephemeral)Runner:每个 Job 都在全新、干净环境执行,跑完即销毁。上一个 Job 的残留(凭据、缓存、被植入的后门)无法污染下一个,显著降低横向渗透与状态泄漏风险——安全 CI 的推荐实践,尤其自托管场景。
- 自托管 Runner 的双刃:能访问内网资源、定制硬件、可能更省钱;但要自己维护、打补丁、保证隔离。对公开仓库用自托管 Runner 跑 fork 代码有重大风险(可能被用来横向渗透内网),一般应避免或严格隔离。
五、供应链加固:SLSA 与 provenance
软件供应链攻击(篡改依赖、篡改构建产物)日益严重。相关机制:
- provenance(来源证明):一份可验证的元数据,记录「某制品是由哪条流水线、基于哪个源码提交、用什么构建过程产出的」。消费者据此验证制品确实来自可信构建、未被篡改。
- SLSA(Supply-chain Levels for Software Artifacts):用分级要求提升从构建到发布链条的可信度,provenance 是其核心产物之一。
- 制品签名:对发布的镜像/包做签名(如 Sigstore/cosign),下游验证签名确保来源。
六、第三方 Action / 组件:用 SHA 固定
引用第三方可复用单元(GitHub Action、GitLab component)时:
- 可变标签(
@v3、@main)可能被上游重新指向被篡改的代码(tag 可被移动、账号可能被劫持),存在供应链投毒风险。 - 用不可变的完整提交 SHA 固定(pinning):
uses: owner/action@a1b2c3d...(40 位 SHA),确保引用的正是你审查过的那份代码,即使上游标签被劫持也不受影响。
这是 CI 供应链加固的推荐实践之一,可配合 Dependabot/Renovate 自动更新被 pin 的 SHA。
七、缓存投毒:共享缓存的隐患
若不受信任的流水线运行(如 fork PR)能写入被后续可信构建复用的缓存,攻击者就能把恶意内容塞进缓存,污染主干构建的产物。防护:
- 按可信度隔离缓存作用域(fork 用独立/只读缓存,不写主干缓存)。
- 缓存键绑定内容哈希、校验缓存完整性。
- 只让受信任的主干 CI 写共享/远程缓存,其余只读(如 Rush 的
RUSH_BUILD_CACHE_WRITE_ALLOWED、各远程缓存的写权限控制)。
八、最小权限贯穿始终
给流水线的令牌与凭据最小必要权限:
- 只需读代码的 Job 不给写权限;只需构建的不给发布权限;部署到 A 环境的凭据不能碰 B 环境。
- GitHub Actions 可用
permissions:收紧GITHUB_TOKEN的权限;云端角色按环境细分。
最小权限把「万一被攻破」的爆炸半径控制在最小范围,是纵深防御的基础一环。