一、3 月的警钟:四起攻击暴露的系统性脆弱
2026 年 3 月 19 日至 3 月 31 日,短短 12 天内发生了四起独立的 CI/CD 供应链攻击[^2]。受害者不是小型项目,而是被数千个企业仓库依赖的流行工具:
- tj-actions/changed-files:一个被超过 23,000 个仓库使用的 GitHub Action,攻击者通过入侵维护者账户推送恶意 commit,窃取 CI 环境中的 secret
- reviewdog/action-setup:代码审查工具的 setup Action,同样通过账户入侵注入恶意代码
- prisma-cloud 相关 Action:云安全扫描工具的 CI 集成组件,攻击者篡改 release tag 指向恶意版本
- 另一未公开命名的流行 Action:通过类似手法影响数百个企业仓库
GitLab 的安全团队在四月份发布的复盘报告中指出,这些攻击的共同模式不是"供应链攻击的新技术",而是"对既有攻击面的系统性利用"[^2]:
Rasec 的分析进一步揭示了一个深层问题:2026 年的攻击者比 2024 年更加"外科手术式"——他们不再大规模污染公共包,而是精准地攻击被大量企业依赖的 CI 工具,利用的是"开发者对 CI 生态的隐式信任"[^3]。当一个开发者在 workflow 中写 uses: tj-actions/changed-files@v45 时,他实际上授予了这个 Action 访问仓库 secret 和代码的完全权限,而这种信任关系几乎没有经过任何安全验证。
二、信任链的五个断裂点
软件从代码到生产部署的供应链可以被抽象为五个环节,每个环节都存在信任断裂的风险:
断裂点 1:代码来源不可验证
Git commit 可以被任何人使用任意作者信息创建。git commit --author="Linus Torvalds <[email protected]>" 不需要任何身份验证。虽然 GPG 签名可以验证作者身份,但 adoption 率极低(GitHub 上不到 5% 的 commit 有 GPG 签名)。
断裂点 2:构建环境不透明 大多数企业使用 GitHub Actions、GitLab CI 或 Jenkins 的托管 runner。这些构建环境对使用者来说是"黑盒"——你不知道 runner 上安装了什么软件、有没有被其他租户污染、有没有被平台管理员植入监控程序。
断裂点 3:构建过程不可复现 即使代码是可信的,构建过程本身也可能引入恶意代码。经典的案例是 XZ Utils 后门(2024):攻击者在构建脚本中注入恶意代码,使得从源码构建出的二进制与源码本身不一致。如果构建过程不可复现,就无法验证"我构建出的二进制是否与官方构建一致"。
断裂点 4:制品分发无签名 容器镜像、npm 包、Python wheel 等软件制品在分发过程中如果没有加密签名,攻击者可以在传输环节(如 CDN 劫持、镜像仓库入侵)替换为恶意版本。
断裂点 5:部署时无验证 Kubernetes 默认会拉取并运行任何指定的容器镜像,即使该镜像没有被签名、即使签名无效。部署系统对制品来源的信任是隐式的,而非显式验证的。
SLSA(Supply-chain Levels for Software Artifacts)框架正是为了系统性地修复这五个断裂点而设计的[^5]。
三、SLSA 框架:供应链安全的成熟度模型
SLSA 由 OpenSSF(Open Source Security Foundation)维护,定义了四个安全等级,从 L1(基础)到 L4(最高):
| 等级 | 出处证明 | 构建环境 | 构建过程 | 人员审查 |
|---|---|---|---|---|
| L1 | 有出处证明 | 无要求 | 无要求 | 无要求 |
| L2 | 有出处证明 | 托管构建 | 有签名 | 无要求 |
| L3 | 有防篡改出处 | 隔离构建 | 可复现 | 无要求 |
| L4 | 有防篡改出处 | 封闭构建 | 可复现 | 双人审查 |
对于企业而言,L3 是建议的最低目标:可复现的构建过程意味着任何人都可以从相同的源码构建出相同的二进制,从而验证官方制品没有被篡改;隔离的构建环境意味着构建过程不受外部网络或人员干扰。
3.1 出处证明(Provenance)
出处证明是 SLSA 的核心概念,它是一份机器可读的元数据,记录了"这个制品是如何被构建出来的"。包括:
- 源码仓库 URL 和 commit SHA
- 构建触发事件(如 pull request #123)
- 使用的构建平台和构建定义文件
- 构建开始和结束时间戳
- 构建过程中使用的依赖项及其哈希
出处证明的价值在于事后溯源和实时验证。当一个安全事件发生时,安全团队可以通过出处证明快速回答:"这个有漏洞的镜像是从哪段代码、通过哪个 pipeline、在什么时间构建的?"
3.2 可复现构建(Reproducible Builds)
可复现构建是 SLSA L3 的关键要求。其技术原理是消除构建过程中的非确定性因素:
实现可复现构建的具体措施包括:
- 使用 lockfile 冻结所有依赖的确切版本和哈希(如
package-lock.json、go.sum、poetry.lock) - 在构建环境中设置
SOURCE_DATE_EPOCH固定时间戳 - 对文件系统进行确定性排序(消除遍历顺序导致的差异)
- 使用最小化容器镜像(如 Wolfi、distroless)作为构建基础,减少环境差异
四、Sigstore:消灭密钥管理的签名革命
传统软件签名依赖 GPG 或 X.509 证书,密钥管理是最大的痛点:谁来保管私钥?私钥泄露后如何轮换?如何验证签名者的身份?
Sigstore 通过"无密钥签名"(keyless signing)彻底改变了这一范式[^6]。其核心组件包括:
Fulcio:一个免费的、自动化的证书颁发机构(CA)。开发者使用 OIDC 身份(如 GitHub 账户、Google 账户、Microsoft 账户)进行认证,Fulcio 颁发一个短期(默认 10 分钟)的代码签名证书。证书中嵌入了开发者的 OIDC 身份标识(如 https://github.com/login/oauth -> username)。
Cosign:用于签名和验证容器镜像、SBOM、git commit 等制品的命令行工具。支持密钥签名和无密钥签名两种模式。
Rekor:签名透明日志,所有签名事件都被记录在一个公开的、不可篡改的日志中(类似 Certificate Transparency)。任何人都可以查询 Rekor 验证签名的存在性和时间戳。
4.1 容器镜像签名实战
以下是一个完整的容器镜像签名和验证流程:
# 1. 构建容器镜像
docker build -t registry.internal/app:v1.2.3 .
docker push registry.internal/app:v1.2.3
# 2. 使用 Cosign 进行无密钥签名
# 这将打开浏览器要求 OIDC 认证(GitHub/Google/Microsoft)
cosign sign --yes registry.internal/app:v1.2.3
# 3. 验证签名
cosign verify registry.internal/app:v1.2.3 \
--certificate-identity-regexp="https://github.com/myorg/.*" \
--certificate-oidc-issuer="https://token.actions.githubusercontent.com"在 CI/CD 环境中,可以使用 GitHub Actions 的 OIDC 身份自动签名,无需人工干预:
# .github/workflows/sign.yml
jobs:
sign:
permissions:
id-token: write # 启用 OIDC
contents: read
steps:
- uses: sigstore/cosign-installer@v3
- run: cosign sign --yes ${{ needs.build.outputs.image_uri }}4.2 SBOM 签名与验证
软件物料清单(SBOM)是供应链安全的另一块基石。签名 SBOM 可以确保其完整性:
# 生成并签名 SBOM
trivy image --format spdx-json -o sbom.spdx.json registry.internal/app:v1.2.3
cosign attest --predicate sbom.spdx.json \
--type spdxjson \
--yes registry.internal/app:v1.2.3
# 验证 SBOM 签名
cosign verify-attestation \
--type spdxjson \
--certificate-identity-regexp="https://github.com/myorg/.*" \
registry.internal/app:v1.2.3五、GitHub Actions 安全加固:从事后补救到预防设计
2026 年 3 月的攻击事件后,GitHub Actions 的安全加固成为企业的优先事项。以下是经过验证的七项关键措施:
5.1 使用 OIDC 替代长期 Secret
传统的 AWS 访问密钥、Azure Service Principal 密码等长期凭证如果被泄露,攻击者可以持续滥用。GitHub Actions 支持 OpenID Connect(OIDC),允许 workflow 通过短期 JWT token 向云提供商认证[^7]:
jobs:
deploy:
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
aws-region: us-east-1
# 无需 AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY这一配置的核心安全价值是:没有长期 secret 可供窃取。JWT token 的有效期仅为 workflow 执行期间,且只能被特定的云 IAM role 接受。
5.2 固定 Action 版本到 Commit SHA
uses: actions/checkout@v4 的语法实际上指向一个可移动的 Git tag。如果 actions/checkout 的维护者账户被入侵,攻击者可以移动 v4 tag 指向恶意 commit。
安全做法是固定到不可变的 commit SHA:
# 不安全
- uses: actions/checkout@v4
# 安全
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2企业可以使用 Dependabot 或 Renovate 自动跟踪 SHA 对应的版本更新,在更新时触发安全审查流程。
5.3 最小化 Workflow 权限
GitHub Actions 默认授予 workflow 对仓库的读写权限。应显式设置为最小权限:
permissions:
contents: read # 仅读取代码
pull-requests: write # 如果需要评论 PR
# 不使用 id-token 时,不要授予 write对于不需要访问仓库代码的 job(如静态分析),可以进一步限制:
jobs:
lint:
permissions: {} # 无任何权限5.4 审计第三方 Action 的供应链
每个 uses: xxx/yyy@... 都是一次信任转移。企业应建立第三方 Action 的审批流程:
- 厂商可信度评估:优先使用官方 Action(如
actions/、aws-actions/、google-github-actions/)或知名安全厂商的 Action(如sigstore/、chainguard-dev/) - 源码审查:对非知名 Action,审查其源码和依赖关系
- 内部镜像:将批准的 Action fork 到企业内部仓库,固定 SHA,仅允许使用内部 fork
Chainguard 提供的加固版 Action[^8] 是一个值得关注的替代方案,它们基于 Wolfi 最小化镜像构建,减少了传统 Action 的依赖攻击面。
六、部署验证:在 K8s 中强制执行签名策略
供应链安全的最后一环是部署验证:确保 Kubernetes 只运行经过签名的、可信的容器镜像。
6.1 Kyverno 镜像签名验证
Kyverno 可以通过 ClusterPolicy 强制要求所有 Pod 使用 Cosign 签名的镜像:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce
rules:
- name: check-cosign-signature
match:
resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "registry.internal/*"
attestors:
- entries:
- keys:
publicKeys: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----当开发者尝试部署一个未签名或使用未知密钥签名的镜像时,Kyverno 会拒绝该请求并返回错误:
Error from server: error when creating "deployment.yaml":
admission webhook "validate.kyverno.svc-fail" denied the request:
resource Deployment/default/app was blocked due to the following failed policies
verify-image-signature:
check-cosign-signature: 'image signature verification failed'
6.2 策略即代码的供应链闭环
将镜像签名验证策略定义为版本控制的代码,并通过 CI/CD 自动部署到集群,可以形成供应链安全的闭环:
七、最小可行方案:六周落地路线图
对于希望快速建立 CI/CD 供应链安全能力的企业,以下是经过验证的六周路线图:
第 1-2 周:基线建立
- 审计所有 GitHub/GitLab workflow,列出使用的第三方 Action 及其版本策略
- 识别 workflow 中使用的长期 secret,制定 OIDC 迁移计划
- 启用 Dependabot/Renovate 自动跟踪 Action 版本更新
第 3-4 周:核心加固
- 将所有 workflow 的 Action 引用从 tag 迁移到 commit SHA
- 将云凭证 secret 迁移到 OIDC 认证
- 为所有 workflow 显式配置最小权限
permissions
第 5 周:签名实施
- 在 CI pipeline 中集成 Cosign,为所有容器镜像添加无密钥签名
- 生成并签名 SBOM(使用 Trivy + Cosign attest)
- 在容器仓库中启用不可变标签(immutable tags)
第 6 周:部署验证
- 在 Kubernetes 中部署 Kyverno 或 Gatekeeper
- 配置镜像签名验证策略,初始为
Audit模式 - 观察一周后切换为
Enforce模式
八、结论:信任不是免费的
2026 年 3 月的供应链攻击事件传递了一个清晰的信号:软件供应链的信任不再是免费赠品,而需要系统性的工程投资来建立和维护。每一个 uses: xxx/yyy@v1、每一个 docker pull、每一个 npm install 都是一次信任转移,而攻击者正在精准地利用这些隐式信任。
SLSA 框架提供了供应链安全的成熟度路线图,Sigstore 消除了签名实施的密钥管理障碍,而 GitHub Actions 的 OIDC 和权限控制则将 CI 环境的安全基线提升到了新的高度。这三者的结合,使得企业可以在不牺牲开发效率的前提下,构建从代码提交到生产部署的完整信任链。
对于安全团队,最关键的洞察是:供应链安全不是安全团队的独角戏,而是需要开发、运维和平台工程的协作。当开发者理解"固定 Action SHA 不是官僚流程而是自我保护",当运维团队将"镜像签名验证"视为部署门禁而非额外负担时,供应链安全才能真正落地。
本文创建日期:2026-06-27 | 状态:draft | 标签:供应链安全, CI/CD, DevSecOps, SLSA, Sigstore
