软件供应链安全工程:从 SLSA、Sigstore 到 SBOM 的生产实践

从 SolarWinds 到 Log4Shell,软件供应链攻击已成为企业安全防线中最薄弱的环节之一。本文深入解析 SLSA 框架、Sigstore 透明日志和 SBOM 在生产环境中的工程落地实践,帮助团队构建可信的软件交付链。


一、背景:为什么供应链安全成为必答题

2020 年的 SolarWinds 事件让全球安全界重新审视软件供应链的风险:攻击者通过污染一家软件厂商的更新管道,成功渗透了数千家企业和政府机构。2021 年末 Log4Shell 漏洞则在另一个维度揭示了纵深依赖的风险——一个 Apache 日志框架的远程代码执行漏洞,几乎影响了互联网上半数以上的 Java 应用。

软件供应链安全的核心挑战包含三个层面:

  1. 上游风险:开源组件被注入后门或包含已知漏洞
  2. 构建风险:CI/CD 管道被篡改,产出物被替换为恶意版本
  3. 交付风险:发布渠道被劫持,用户下载到被篡改的二进制文件

传统的边界防御模型在这些场景下几乎完全失效——信任链延伸到构建环境的每一行脚本、每一个开发者工具。要系统性地解决这些问题,需要建立从源码到二进制的全链路可验证体系。这正是 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

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部