软件供应链安全

软件供应链攻击防御全指南:SLSA 框架、SBOM 物料清单、Sigstore/Cosign 无密钥签名、依赖安全、容器镜像供应链,以及 SolarWinds 案例深度复盘。

开篇:供应链——软件安全的阿喀琉斯之踵

2020 年 SolarWinds Orion 供应链攻击震惊了全球网络安全界:攻击者通过篡改 SolarWinds 的构建系统,在 Orion 软件更新中植入后门 SUNBURST,影响了包括美国财政部、国土安全部在内的 18,000 多个组织。2021 年 Codecov Bash Uploader 被篡改,导致数千家企业的 CI 密钥泄露。这些事件揭示了一个残酷事实:即使你的代码完美无瑕,供应链上游的任何一个环节被攻陷,都可能让你成为受害者

软件供应链安全(Supply Chain Security)关注的是从源代码到生产部署的整个链路中,软件制品的完整性与可信性。本章将介绍 SLSA 框架、SBOM 物料清单、Sigstore 无密钥签名等新兴标准,以及依赖管理、容器镜像、CI/CD 流水线的具体防护实践。


一、软件供应链攻击全景

1.1 攻击向量分类

攻击类型描述典型案例
依赖混淆上传与内部包同名的公共包,pip/npm 优先拉取公共包Alex Birsan 2021
Typosquatting注册与流行包相似的名称(如 reqeusts vs requests大量恶意 PyPI/npm 包
恶意包注入合法维护者账号被盗或主动上传恶意版本event-stream 2018
构建系统篡改入侵 CI/CD 或构建服务器,在编译时注入恶意代码SolarWinds 2020
源码仓库入侵通过弱凭证或社工获取源码仓库写权限PHP git 服务器 2021
镜像污染在 Docker Hub 上传与官方镜像混淆的恶意镜像大量挖矿镜像
编译器攻击在编译器/工具链中植入后门(Trusting Trust 问题)XCodeGhost 2015

1.2 SolarWinds 深度复盘

攻击链(Kill Chain):
1. 侦察 → 发现 SolarWinds 使用非托管的 Microsoft 365 环境
2. 初始访问 → 通过密码喷洒获取 Microsoft 365 账户
3. 权限提升 → 利用 SAML 令牌伪造绕过 MFA
4. 横向移动 → 访问构建服务器(SolarWinds.Orion.Core.BusinessLayer.dll)
5. 持久化 → 修改构建流程,在每次编译时自动注入 SUNBURST 后门
6. 影响扩散 → 通过官方更新渠道分发至 18,000+ 客户

关键教训

  • 构建环境必须与开发/办公网络隔离
  • 构建过程需要可审计的日志和不可变制品
  • 代码签名不等于安全,签名私钥也可能被盗

一句话总结:供应链攻击的破坏力在于利用"信任链"——受害者信任的软件更新渠道变成了攻击者的分发网络。


二、SLSA 框架:供应链安全等级

SLSA(Supply-chain Levels for Software Artifacts)是由 Google 开源的安全框架,定义了 4 个递增的安全等级。

2.1 SLSA Levels

等级要求防护效果
Level 1制品需附带出处信息(Provenance)可追溯来源,无法防止篡改
Level 2使用版本控制和托管构建服务防止个人笔记本上的随意构建
Level 3构建环境隔离、不可变参数、无外部网络防止构建时注入,需要双因素认证
Level 4双人审查、 hermetic 构建(完全可复现)、可验证的 SBOM最高级别,可审计每一步

2.2 SLSA Provenance 示例

{
  "_type": "https://in-toto.io/Statement/v0.1",
  "subject": [{
    "name": "app-v1.2.3.tar.gz",
    "digest": { "sha256": "abc123..." }
  }],
  "predicateType": "https://slsa.dev/provenance/v0.2",
  "predicate": {
    "builder": { "id": "https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main" },
    "buildType": "https://github.com/slsa-framework/github-actions-buildtypes/workflow/v1",
    "invocation": {
      "configSource": {
        "uri": "git+https://github.com/myorg/myrepo@refs/heads/main",
        "digest": { "sha1": "def456..." }
      }
    },
    "metadata": {
      "buildInvocationId": "https://github.com/myorg/myrepo/actions/runs/123456789",
      "completeness": {
        "parameters": true,
        "environment": true,
        "materials": false
      }
    }
  }
}

一句话总结:SLSA 提供了从 L1 到 L4 的渐进式安全升级路径,即使是 L1 的出处记录也能大幅提升供应链的可追溯性。


三、SBOM:软件物料清单

SBOM(Software Bill of Materials)是软件成分的清单,类似食品包装的配料表。

3.1 两大标准格式

格式特点适用场景
SPDXLinux 基金会标准,ISO/IEC 5962:2021企业合规、许可证管理
CycloneDXOWASP 项目,JSON/XML 友好安全分析、漏洞管理

3.2 生成 SBOM

# Syft:生成容器镜像 SBOM
syft myapp:latest -o spdx-json > sbom.spdx.json
syft myapp:latest -o cyclonedx-json > sbom.cyclonedx.json

# Trivy:扫描并生成 SBOM
trivy image --format cyclonedx --output sbom.json myapp:latest

# npm:内置 SBOM 生成
npm sbom --format cyclonedx-json --output sbom.json

# Go:使用 bomtools
go install github.com/google/osv-scanner/cmd/osv-scanner@latest
osv-scanner --format json -r . > vulnerabilities.json

3.3 SBOM 内容示例(CycloneDX)

{
  "bomFormat": "CycloneDX",
  "specVersion": "1.5",
  "components": [
    {
      "type": "library",
      "name": "lodash",
      "version": "4.17.21",
      "purl": "pkg:npm/lodash@4.17.21",
      "licenses": [{ "license": { "id": "MIT" } }],
      "supplier": { "name": "John-David Dalton" }
    },
    {
      "type": "library",
      "name": "express",
      "version": "4.18.2",
      "purl": "pkg:npm/express@4.18.2",
      "licenses": [{ "license": { "id": "MIT" } }],
      "vulnerabilities": [
        {
          "id": "CVE-2022-XXXX",
          "source": { "name": "NVD", "url": "https://nvd.nist.gov/..." },
          "ratings": [{ "source": { "name": "CVSSv3" }, "score": 7.5 }]
        }
      ]
    }
  ]
}

一句话总结:SBOM 让软件的"配料"透明化,是供应链安全的基础构件,也是美国政府对关键软件供应商的强制要求(EO 14028)。


四、Sigstore 与 Cosign:无密钥签名

传统代码签名的痛点在于密钥管理——私钥被盗意味着签名信任链崩塌。Sigstore 提供了一种无需长期私钥的签名方案。

4.1 Sigstore 三大组件

组件功能
Fulcio基于 OIDC 的短期证书颁发机构(CA)
Rekor透明日志(Transparency Log),公开可审计的签名记录
Cosign客户端工具,用于签名和验证容器镜像/制品

4.2 Cosign 实战

# 安装 Cosign
curl -O -L https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign

# 签名镜像(无需长期私钥,使用 OIDC 登录)
cosign sign --yes myregistry/myapp:v1.2.3
# 会自动打开浏览器进行 GitHub/Google/Microsoft 认证

# 验证签名
cosign verify myregistry/myapp:v1.2.3 \
  --certificate-identity=user@example.com \
  --certificate-oidc-issuer=https://github.com/login/oauth

# 在 CI 中签名(GitHub Actions 示例)
# .github/workflows/sign.yml
- name: Sign container image
  uses: sigstore/cosign-installer@v3
- name: Sign the published Docker image
  env:
    COSIGN_EXPERIMENTAL: 1
  run: cosign sign --yes ${{ steps.meta.outputs.tags }}

4.3 Kubernetes 中验证镜像签名

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
spec:
  validationFailureAction: Enforce
  rules:
  - name: check-signature
    match:
      resources:
        kinds:
        - Pod
    verifyImages:
    - imageReferences:
      - "myregistry/*"
      attestors:
      - entries:
        - keyless:
            subject: "*@example.com"
            issuer: "https://github.com/login/oauth"

一句话总结:Sigstore/Cosign 通过 OIDC 短期证书和透明日志,彻底改变了代码签名的信任模型——不再需要保护长期私钥,签名的历史公开可审计。


五、依赖安全管理

5.1 Lock 文件的重要性

# ❌ 没有 lock 文件,每次构建可能拉取不同版本
# requirements.txt (无版本锁定)
requests
numpy

# ✅ 使用 lock 文件确保可复现构建
# requirements.txt (锁定版本)
requests==2.31.0
numpy==1.24.3

# 更好的方案: poetry/pnpm 自动 lock
# poetry.lock / pnpm-lock.yaml

5.2 自动化依赖扫描

# .github/workflows/dependency-review.yml
name: Dependency Review
on: [pull_request]

jobs:
  dependency-review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/dependency-review-action@v3
        with:
          fail-on-severity: moderate

# Trivy 扫描(含 SBOM 生成)
- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: 'fs'
    format: 'sarif'
    output: 'trivy-results.sarif'

# Dependabot 配置
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"
    open-pull-requests-limit: 10
    ignore:
      - dependency-name: "*"
        update-types: ["version-update:semver-major"]

5.3 最小权限依赖原则

// ❌ 引入整个工具包,只使用一个函数
{
  "dependencies": {
    "lodash": "^4.17.21"  // 70KB,只用 _.debounce
  }
}

// ✅ 按需引入子包或替代方案
{
  "dependencies": {
    "lodash.debounce": "^4.0.8"  // 仅引入所需函数
    // 或
    "just-debounce-it": "^3.2.0"  // 更轻量的替代
  }
}

一句话总结:依赖安全的核心是"知道你在用什么"——lock 文件确保可复现,自动化扫描确保及时预警,最小权限原则减少攻击面。


六、Git 与 CI/CD 供应链安全

6.1 GPG 签名提交

# 生成 GPG 密钥
gpg --full-generate-key

# 配置 Git 使用 GPG
git config --global user.signingkey YOUR_KEY_ID
git config --global commit.gpgsign true

# 签名提交
git commit -S -m "Signed commit"

# 验证签名
git verify-commit HEAD

# GitHub 上启用 vigilant mode(显示未签名提交警告)
# Settings → SSH and GPG keys → Vigilant mode

6.2 Branch 保护与 CODEOWNERS

# .github/CODEOWNERS
# 全局默认审查者
* @team-backend

# 安全相关文件需要安全团队审查
/src/auth/ @security-team
/src/crypto/ @security-team
/.github/workflows/ @devops-team @security-team

# 数据库迁移需要 DBA 审查
/db/migrations/ @dba-team

6.3 Pre-commit Hooks

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: trailing-whitespace
      - id: check-merge-conflict

  # 密钥扫描
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.4.0
    hooks:
      - id: detect-secrets

  # 依赖漏洞扫描
  - repo: https://github.com/pypa/pip-audit
    rev: v2.6.1
    hooks:
      - id: pip-audit
        args: ["--requirement", "requirements.txt"]

  # 容器镜像扫描(如果提交 Dockerfile)
  - repo: https://github.com/bridgecrewio/checkov
    rev: 2.4.0
    hooks:
      - id: checkov-dockerfile

一句话总结:Git 层面的防护(签名、审查、pre-commit)是供应链安全的第一道防线,在代码合并前阻断绝大部分风险。


七、容器镜像供应链

7.1 安全镜像构建

# ❌ 使用完整基础镜像
FROM node:18  # 947MB,包含大量不需要的工具

# ✅ 使用 distroless 或 Alpine
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production

FROM gcr.io/distroless/nodejs18-debian11  # 146MB,无 shell
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
USER nonroot:nonroot
EXPOSE 3000
CMD ["server.js"]

7.2 镜像扫描与签名

# Trivy 镜像扫描
trivy image --severity HIGH,CRITICAL myapp:latest

# Snyk 扫描
snyk container test myapp:latest

# 签名镜像
cosign sign --yes myregistry/myapp:v1.2.3

# 在 Kubernetes 中强制只运行签名镜像
# 配合 Kyverno/OPA Gatekeeper 策略

一句话总结:容器安全的供应链维度包括"构建时最小化"(distroless)、“分发时签名”(Cosign)和"运行时验证"(Kyverno 策略)三个环节。


FAQ

Q1: SBOM 会泄露软件的敏感信息吗?

SBOM 包含的是依赖组件清单,不包含源代码或业务逻辑。事实上,SBOM 的透明性恰恰是为了让安全研究人员和依赖方更好地评估风险,这是安全领域的共识做法。

Q2: SLSA Level 4 值得追求吗?

对于绝大多数项目,L2-L3 已经足够。L4 的 hermetic 构建和双人审查成本较高,建议用于核心基础设施、安全关键组件或支付/金融类应用。

Q3: Cosign 的短期证书有时间限制吗?

Fulcio 颁发的证书有效期通常为 10 分钟,签名后证书过期不影响已存在的签名验证——验证依赖的是 Rekor 透明日志中的永久记录。

Q4: 依赖混淆攻击怎么防御?

  1. 配置包管理器优先查询私有仓库(如 Verdaccio/Nexus);
  2. 使用作用域包(@company/package);
  3. 在私有包名称前注册商标或占位符公共包。

Q5: 如何知道我的项目受不受 SolarWinds 类攻击的影响?

  1. 盘点所有第三方依赖和构建工具;
  2. 验证构建环境的隔离性(是否有办公网络访问权限);
  3. 检查 CI/CD 系统的权限配置(是否使用了最小权限原则);
  4. 实施 SLSA L2+ 的出处记录要求。

Q6: 开源项目如何低成本实现供应链安全?

  • GitHub 免费功能:Dependency Graph、Dependabot alerts、CodeQL (public repos)
  • Sigstore/Cosign:完全免费的无密钥签名
  • SLSA GitHub Generator:自动生成出处证明
  • OpenSSF Scorecard:自动评估项目安全实践

相关阅读

  • https://plumephp.com/security-devsecops-pipeline/ — DevSecOps 流水线实践
  • https://plumephp.com/security-sast-dast-sca/ — SAST/DAST/SCA 代码安全分析
  • https://plumephp.com/security-container-security/ — 容器安全最佳实践

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「安全」更多文章

  1. Kubernetes安全体系:RBAC、PodSecurity与NetworkPolicy实战
  2. 安全合规与数据保护
  3. 渗透测试与红蓝对抗