开篇:供应链——软件安全的阿喀琉斯之踵
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 两大标准格式
| 格式 | 特点 | 适用场景 |
|---|---|---|
| SPDX | Linux 基金会标准,ISO/IEC 5962:2021 | 企业合规、许可证管理 |
| CycloneDX | OWASP 项目,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: 依赖混淆攻击怎么防御?
- 配置包管理器优先查询私有仓库(如 Verdaccio/Nexus);
- 使用作用域包(
@company/package); - 在私有包名称前注册商标或占位符公共包。
Q5: 如何知道我的项目受不受 SolarWinds 类攻击的影响?
- 盘点所有第三方依赖和构建工具;
- 验证构建环境的隔离性(是否有办公网络访问权限);
- 检查 CI/CD 系统的权限配置(是否使用了最小权限原则);
- 实施 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/ — 容器安全最佳实践
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。