软件供应链安全工程:从 SLSA、Sigstore 到 SBOM 的生产实践
从 SolarWinds 到 Log4Shell,软件供应链攻击已成为企业安全防线中最薄弱的环节之一。本文深入解析 SLSA 框架、Sigstore 透明日志和 SBOM 在生产环境中的工程落地实践,帮助团队构建可信的软件交付链。
一、背景:为什么供应链安全成为必答题
2020 年的 SolarWinds 事件让全球安全界重新审视软件供应链的风险:攻击者通过污染一家软件厂商的更新管道,成功渗透了数千家企业和政府机构。2021 年末 Log4Shell 漏洞则在另一个维度揭示了纵深依赖的风险——一个 Apache 日志框架的远程代码执行漏洞,几乎影响了互联网上半数以上的 Java 应用。
软件供应链安全的核心挑战包含三个层面:
- 上游风险:开源组件被注入后门或包含已知漏洞
- 构建风险:CI/CD 管道被篡改,产出物被替换为恶意版本
- 交付风险:发布渠道被劫持,用户下载到被篡改的二进制文件
传统的边界防御模型在这些场景下几乎完全失效——信任链延伸到构建环境的每一行脚本、每一个开发者工具。要系统性地解决这些问题,需要建立从源码到二进制的全链路可验证体系。这正是 SLSA、Sigstore 和 SBOM 三者协同工作的价值所在。
二、SLSA 框架:构建产物的溯源与分级
SLSA(Supply-chain Levels for Software Artifacts)是一套用于评估软件供应链安全性的分级框架,由 Google 联合开源社区推动。它将软件供应链安全划分为四个等级(Level 1 至 Level 4),每一级别对构建过程的可追溯性和防篡改能力提出递进式要求。
2.1 SLSA 核心概念
SLSA 的核心是 provenance(溯源数据),它记录了软件工件是"从哪里来、由谁构建、使用什么方式构建"。一份标准的 SLSA provenance 包含以下关键字段:
{
"_type": "https://in-toto.io/Statement/v0.1",
"subject": [
{
"name": "my-app-v2.3.1.tar.gz",
"digest": {"sha256": "abc123..."}
}
],
"predicateType": "https://slsa.dev/provenance/v0.2",
"predicate": {
"builder": {
"id": "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_go_s300s.slsa3.yml@refs/tags/v1.2.0"
},
"buildType": "https://github.com/slsa-framework/slsa-github-generator/go@v1",
"invocation": {
"configSource": {
"uri": "git+https://github.com/example/my-app@refs/tags/v2.3.1",
"digest": {"sha1": "def456..."},
"entryPoint": "build.yml"
},
"parameters": {},
"environment": {}
},
"metadata": {
"buildStartedOn": "2026-09-15T10:00:00Z",
"buildFinishedOn": "2026-09-15T10:05:00Z",
"completeness": {
"parameters": true,
"environment": false,
"materials": true
},
"reproducible": false
},
"materials": [
{
"uri": "git+https://github.com/example/my-app@refs/tags/v2.3.1",
"digest": {"sha1": "def456..."}
}
]
}
}
2.2 四个等级的递进关系
| 等级 | 名称 | 核心要求 | 典型实现 |
|---|---|---|---|
| L1 | 可追溯 | 构建过程生成 provenance 元数据 | GitHub Actions + SLSA generator |
| L2 | 托管构建 | 使用可信托管构建平台,签名 provenance | Cloud Build / GitHub OIDC |
| L3 | 强化平台 | 构建平台提供防 tamper 能力,隔离构建环境 | SLSA GitHub Builder / Container Builder |
| L4 | 双人审核 + 可复现 | 独立双人审查 + 确定性构建(bit-for-bit) | 最高安全级别,多用于关键基础设施 |
2.3 GitHub Actions 中生成 SLSA Provenance
以下是一个在 GitHub Actions 工作流中生成 SLSA Level 3 provenance 的典型配置:
# .github/workflows/build-slsa.yml
name: Build with SLSA Provenance
on:
push:
tags: ['v*']
workflow_dispatch:
permissions:
id-token: write # 用于 OIDC 认证
contents: read
actions: read
jobs:
build:
runs-on: ubuntu-latest
outputs:
hashes: ${{ steps.hash.outputs.hashes }}
steps:
- uses: actions/checkout@v4
- name: Build artifact
run: |
make build
mkdir -p dist
cp bin/my-app dist/my-app-${{ github.ref_name }}
- name: Generate subject for provenance
id: hash
run: |
cd dist
sha256sum my-app-* > checksums.txt
echo "hashes=$(sha256sum my-app-* | base64 -w0)" >> "$GITHUB_OUTPUT"
provenance:
needs: build
permissions:
actions: read
id-token: write
contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
with:
base64-subjects: "${{ needs.build.outputs.hashes }}"
release:
needs: [build, provenance]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download artifacts and provenance
uses: actions/download-artifact@v4
with:
name: ${{ needs.provenance.outputs.provenance-name }}
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: |
dist/*
${{ needs.provenance.outputs.provenance-name }}
执行这个工作流后,每次 tag 发布都会自动生成一份 signed provenance 文件(multiple.intoto.jsonl),其中包含了完整的构建溯源数据。消费者可以使用 SLSA verifier 验证此文件的合法性。
2.4 验证 SLSA Provenance
生产环境中,下游消费者可以用如下命令验证 SLSA provenance:
# 安装 SLSA verifier
go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier@latest
# 验证 GitHub Actions 构建产物的 provenance
slsa-verifier verify-artifact \
./my-app-v2.3.1 \
--provenance-path ./multiple.intoto.jsonl \
--source-uri github.com/example/my-app \
--source-versioned v2.3.1 \
--builder-id https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_go_s300s.slsa3.yml
验证过程包括: - 检查 provenance 的数字签名是否来自可信构建器 - 确认工件哈希值与 provenance 中记录的一致 - 确认构建来源(仓库、commit、分支/标签)与预期一致
三、Sigstore:简化软件签名的透明基础设施
传统软件签名依赖中心化 CA 体系,证书管理复杂、成本高,且难以实现签名过程的可审计性。Sigstore 的出现彻底改变了这一局面——它提供了一套免费、透明、基于开放日志的签名基础设施,核心组件包括 Cosign、Rekor 和 Fulcio。
3.1 Sigstore 架构组件
- Fulcio:免费的证书颁发机构,基于 OIDC 身份(如 GitHub、Google 邮箱)签发短期证书,有效期仅 10 分钟到 24 小时
- Rekor:透明日志服务,记录所有签名操作,提供不可篡改的公开审核追踪
- Cosign:签名与验证工具,支持容器镜像、二进制文件、OCI 工件等
┌──────────┐ OIDC Token ┌───────────┐
│ Developer│ ──────────────────► │ Fulcio │
│ (GitHub) │ ◄────────────────── │ (CA) │
└────┬─────┘ Short-lived Cert └───────────┘
│
│ Sign artifact + write to log
▼
┌──────────┐ Transparency ┌───────────┐
│ Cosign │ ──────────────────► │ Rekor │
│ Client │ Public Log │ (Log) │
└──────────┘ └───────────┘
3.2 使用 Cosign 签名容器镜像
Cosign 最大的优势之一是支持 keyless 签名(无需管理长期私钥),配置步骤如下:
# 安装 Cosign
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
# Keyless 签名容器镜像(基于 GitHub OIDC 身份)
cosign sign \
--oidc-issuer=https://token.actions.githubusercontent.com \
ghcr.io/example/my-app:v2.3.1
# 验证签名
cosign verify \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--certificate-identity=mailto:[email protected] \
ghcr.io/example/my-app:v2.3.1
签名过程中 Cosign 自动完成: 1. 向 Fulcio 申请短期签名证书(绑定 OIDC 身份) 2. 对容器镜像 digest 进行签名 3. 将签名记录写入 Rekor 透明日志 4. 将签名附注到镜像 OCI 元数据
3.3 在生产集群中强制执行签名验证
通过 Kubernetes 的 admission controller(如 Conftest 或 Kyverno):
# kyverno 策略:只允许经过 Sigstore 签名验证的镜像
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signatures
spec:
validationFailureAction: Enforce
background: true
webhookTimeoutSeconds: 30
failurePolicy: Fail
rules:
- name: check-sigstore-signature
match:
any:
- resources:
kinds:
- Pod
namespaces:
- production
verifyImages:
- imageReferences:
- "ghcr.io/example/*"
attestations:
- type: https://cosign.sigstore.dev/attestation/v1
conditions:
- all:
- key: "{{ repo_uri }}"
operator: Equals
value: "https://github.com/example/my-app"
mutateDigest: true
required: true
keys:
# 可选:指定信任的根证书或密钥
rekor:
url: https://rekor.sigstore.dev
当开发者尝试部署未签名的镜像时,Kubernetes API Server 会直接拒绝请求,从集群入口杜绝未验证代码的执行。
3.4 Sigstore 的 TSA 时间戳集成
某些合规场景需要独立的时间戳凭据(而非仅依赖 Rekor 日志中的集成时间)。Cosign 支持使用信任的时间戳机构(TSA):
# 使用 freetsa.org 时间戳签名
cosign sign --timestamp-server-url=https://freetsa.org/tsr \
ghcr.io/example/my-app:v2.3.1
# 验证时间戳
cosign verify --timestamp-certificate=tsa.crt \
ghcr.io/example/my-app:v2.3.1
四、SBOM:软件物料清单的工程实践
SBOM(Software Bill of Materials)是软件供应链安全的第三支柱。如果说 SLSA 解决的是"这个工件来自哪里、由谁签名"的问题,那么 SBOM 回答的是"这个工件里面包含什么组件"。
4.1 SBOM 格式对比
| 格式 | 标准 | 特点 | 适用场景 |
|---|---|---|---|
| SPDX | ISO/IEC 5962:2021 | 功能最完整,支持 License 分析 | 合规审计 |
| CycloneDX | OWASP 项目 | 轻量化,侧重漏洞管理 | 日常安全运营 |
| SWID | ISO/IEC 19770-2 | 安装后识别 | 软件资产管理 |
目前 SPDX 和 CycloneDX 是生产环境中最常用的两种格式,两者之间也有成熟的转换工具(如 cyclonedx-spdx-cli 和 spdx-to-cyclonedx)。
4.2 生成 SBOM 的工具链
Syft(Anchore 出品)
# 从容器镜像生成 SBOM
syft ghcr.io/example/my-app:v2.3.1 -o syft-json > sbom.syft.json
syft ghcr.io/example/my-app:v2.3.1 -o spdx-json > sbom.spdx.json
syft ghcr.io/example/my-app:v2.3.1 -o cyclonedx-json > sbom.cyclonedx.json
# 从源码仓库生成 SBOM(分析依赖文件)
syft dir:. -o cyclonedx-json
# 从已安装系统生成
syft / -o spdx-tag-value
Trivy(Aqua Security 出品)
# 同时扫描漏洞和生成 SBOM
trivy image --format cyclonedx --output sbom.cdx.json \
ghcr.io/example/my-app:v2.3.1
# 仅生成 SBOM(不扫描漏洞)
trivy image --sbom-sources --format spdx-json \
--output sbom.spdx.json \
ghcr.io/example/my-app:v2.3.1
4.3 典型 SBOM 结构(CycloneDX JSON)
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:550e8400-e29b-41d4-a716-446655440000",
"version": 1,
"metadata": {
"timestamp": "2026-09-15T10:00:00Z",
"tools": [
{"vendor": "anchore", "name": "syft", "version": "0.100.0"}
],
"component": {
"type": "application",
"name": "my-app",
"version": "2.3.1",
"purl": "pkg:docker/example/[email protected]"
}
},
"components": [
{
"type": "library",
"name": "log4j-core",
"version": "2.17.1",
"purl": "pkg:maven/org.apache.logging.log4j/[email protected]",
"hashes": [
{"alg": "SHA-256", "content": "abc123..."}
]
},
{
"type": "library",
"name": "openssl",
"version": "3.0.11",
"purl": "pkg:deb/debian/[email protected]"
}
],
"dependencies": [
{"ref": "pkg:docker/example/[email protected]", "dependsOn": [
"pkg:maven/org.apache.logging.log4j/[email protected]",
"pkg:deb/debian/[email protected]"
]}
]
}
4.4 从 SBOM 中提取漏洞情报(VEX)
SBOM 与漏洞扫描结合可生成 VEX(Vulnerability Exploitability Exchange)声明,帮助运维团队快速判断哪些已知漏洞对当前环境真正构成威胁:
# Trivy 生成包含漏洞信息的 SBOM
trivy sbom --format cyclonedx --scanners vuln \
--output sbom-with-vulns.json \
ghcr.io/example/my-app:v2.3.1
# 使用 grype 单独扫描 SBOM 文件
grype sbom:sbom.spdx.json --output table
# Grype 结合 VEX 文件过滤误报
grype sbom:sbom.spdx.json \
--vex=vex.json \
--fail-on critical
VEX 示例(声明某 CVE 对当前部署无实际影响):
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"metadata": {
"vexStatements": [
{
"vulnerabilityName": "CVE-2023-12345",
"status": "not_affected",
"justification": "code_not_present",
"impactStatement": "漏洞代码路径在当前配置下不可达"
}
]
}
}
五、生产环境落地实践:CI/CD 全链路集成
5.1 完整的供应链安全管道设计
一个成熟的供应链安全管道通常包含以下阶段:
开发者提交 → 代码扫描(SAST)
↓
依赖审计(SCA + SBOM生成)
↓
隔离构建(GitHub / Cloud Build)
↓
签名(Cosign + SLSA Provenance)
↓
容器镜像签名 + SBOM 附加
↓
准入控制(Kyverno/Conftest) → Kubernetes 集群
5.2 完整的 GitHub Actions 工作流示例
# .github/workflows/secure-release.yml
name: Secure Build & Release Pipeline
on:
push:
tags: ['v*']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
permissions:
id-token: write
contents: read
packages: write
actions: read
jobs:
# ─── Stage 1: 依赖安全审计 ───
dependency-audit:
runs-on: ubuntu-latest
outputs:
sbom-hash: ${{ steps.sbom.outputs.digest }}
steps:
- uses: actions/checkout@v4
- name: Install Syft
uses: anchore/sbom-action/download-syft@v0
- name: Generate SBOM
id: sbom
uses: anchore/sbom-action@v0
with:
format: spdx-json
output-file: sbom.spdx.json
- name: Vulnerability Scan
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
severity: 'CRITICAL,HIGH'
exit-code: '1'
ignore-unfixed: true
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.json
# ─── Stage 2: 构建 + SLSA Provenance ───
build-and-sign:
needs: dependency-audit
runs-on: ubuntu-latest
outputs:
image-digest: ${{ steps.build.outputs.digest }}
hashes: ${{ steps.hash.outputs.hashes }}
steps:
- uses: actions/checkout@v4
- name: Download SBOM
uses: actions/download-artifact@v4
with:
name: sbom
path: ./sbom
- name: Build container
uses: docker/build-push-action@v5
id: build
with:
context: .
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
sbom: true
provenance: true
- name: Create binary checksums
id: hash
run: |
echo "hashes=${{ steps.build.outputs.digest }}" >> "$GITHUB_OUTPUT"
# ─── Stage 3: Cosign 签名 ───
sign:
needs: build-and-sign
runs-on: ubuntu-latest
permissions:
id-token: write
packages: write
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Sign container image
run: |
cosign sign --yes \
--oidc-issuer=https://token.actions.githubusercontent.com \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build-and-sign.outputs.image-digest }}
- name: Attach SBOM to image
uses: anchore/sbom-action@v0
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build-and-sign.outputs.image-digest }}
format: spdx-json
# ─── Stage 4: SLSA Provenance ───
provenance:
needs: build-and-sign
permissions:
actions: read
id-token: write
contents: write
uses: slsa-framework/slsa-github-generator/.github/workflows/[email protected]
with:
image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
digest: "${{ needs.build-and-sign.outputs.image-digest }}"
registry-username: ${{ github.actor }}
secrets:
registry-password: ${{ secrets.GITHUB_TOKEN }}
5.3 签名验证脚本(自动化部署前检查)
#!/bin/bash
# verify-deployment.sh
# 部署前的供应链安全验证脚本
set -euo pipefail
IMAGE_REF="${1?"Usage: verify-deployment.sh <image-ref>"}"
EXPECTED_SOURCE="github.com/example/my-app"
echo "🔍 验证 ${IMAGE_REF} 的供应链安全属性..."
# 1. 检查 SBOM 是否存在
echo " [1/4] 检查 SBOM..."
if ! syft-attest=$(cosign download sbom "${IMAGE_REF}" 2>/dev/null); then
echo "❌ 缺少 SBOM 附件,拒绝部署"
exit 1
fi
echo " ✅ SBOM 已找到"
# 2. 验证漏洞信息
echo " [2/4] 漏洞扫描..."
VULN_COUNT=$(cosign download sbom "${IMAGE_REF}" | \
grype sbom:- --output json 2>/dev/null | \
jq -r '.matches | length')
if [[ "${VULN_COUNT}" -gt 10 ]]; then
echo "❌ 发现 ${VULN_COUNT} 个未缓解漏洞,超出阈值"
exit 1
fi
echo " ✅ 漏洞数量在可接受范围内"
# 3. 验证 Cosign 签名
echo " [3/4] 验证签名..."
if ! cosign verify \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
--certificate-identity-regexp="https://github.com/${EXPECTED_SOURCE}" \
"${IMAGE_REF}" 2>/dev/null; then
echo "❌ 签名验证失败"
exit 1
fi
echo " ✅ 签名有效"
# 4. 验证 SLSA Provenance
echo " [4/4] 验证 SLSA Provenance..."
PROVENANCE_PATH=$(mktemp)
cosign download attestation \
--predicate-type "https://slsa.dev/provenance/v0.2" \
"${IMAGE_REF}" > "${PROVENANCE_PATH}"
BUILDER_ID=$(jq -r '.predicate.builder.id' "${PROVENANCE_PATH}")
if [[ ! "${BUILDER_ID}" =~ slsa-framework ]]; then
echo "❌ 不可信的构建器: ${BUILDER_ID}"
exit 1
fi
echo " ✅ 构建来源可信: ${BUILDER_ID}"
echo ""
echo "✅ 所有检查通过,允许部署 ${IMAGE_REF}"
六、最佳实践与常见陷阱
6.1 不要忽视构建环境安全
即使使用了 Sigstore 构建签名,如果构建环境本身被攻击者控制(例如通过恶意的 GitHub Actions 脚本窃取 OIDC token),签名也形同虚设。关键实践包括:
- 在 GitHub Actions 中使用
permissions字段严格限制 token scope - 对第三方 actions 使用 commit SHA 而非 tag 引用(防 tag 替换攻击)
- 启用 GitHub 的 "Require approval for all external workflows" 设置
- 定期审计 CI/CD 管道的 secrets 和 variables
6.2 SBOM 的持续更新问题
SBOM 不是"生成一次即可"的静态文档。每次依赖更新(包括安全补丁)都应触发 SBOM 重新生成和漏洞复检。实践中应将 SBOM 生成管道化:
# 每次 PR 合并到 main 后自动更新 SBOM
on:
push:
branches: [main]
jobs:
update-sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: anchore/sbom-action@v0
with:
format: cyclonedx-json
output-file: sbom.json
- name: Commit updated SBOM
run: |
git config user.name "SBOM Bot"
git config user.email "[email protected]"
git add sbom.json
git diff --quiet --cached || git commit -m "chore: update SBOM"
git push
6.3 签名密钥轮换策略
虽然 Sigstore 的短期证书自动处理了大多数密钥管理问题,但使用长期密钥(如 GPG 或硬件 HSM)时轮换策略至关重要:
# 使用 KMS 后端时(如 GCP KMS)
cosign generate-key-pair --kms gcpkms://projects/my-project/locations/global/keyRings/my-keyring/cryptoKeys/my-key
# 密钥轮换:并行签名验证过渡期(7-14 天)
cosign sign --key cosign-old.key docker.io/example/app:v2.4.0
# 在新密钥可用后
cosign sign --key cosign-new.key docker.io/example/app:v2.4.0
# 验证时允许任一(过渡期内)
cosign verify --key cosign-old.key image || cosign verify --key cosign-new.key image
七、总结
软件供应链安全是一套系统工程,单一技术无法解决所有问题。三者的分工与协作关系可以总结如下:
| 关注点 | 核心工具 | 解决的问题 |
|---|---|---|
| 构建来源可信性 | SLSA + Provenance | "这个工件来自哪里、谁构建了它" |
| 工件完整性 | Sigstore + Cosign | "这个工件被签名了吗、签名有效吗" |
| 组件可见性 | SBOM (Syft/Scaffold) | "这个工件包含哪些组件、有什么风险" |
在 206 年 10 月的当下,美国行政命令 14028 和欧盟网络韧性法案(CRA)已经将 SBOM 和供应链安全从"最佳实践"提升为"合规要求"。对于面向国际市场的软件产品团队而言,提前布局这三者的工程化落地,既是安全防御能力的建设,也是商业竞争力的保障。
落地建议:从 SLSA Level 1 起步(最低成本引入 provenvenance 元数据),逐步推进到 Level 2-3;Sigstore 签名可立即集成到现有 CI 管道;SBOM 则建议在发布流水线中自动生成并关联到制品仓库。循序渐进,不必追求一步到位——有 provenance 比没有好,有 SBOM 比在漏洞爆发时手足无措强得多。
参考资源: - SLSA 规范: https://slsa.dev/spec/v1.0/ - Sigstore 项目: https://www.sigstore.dev/ - OWASP SBOM 指南: https://owasp.org/www-community/Component_Analysis - CISA SBOM 资源: https://www.cisa.gov/sbom

发表评论 取消回复