“生产环境的组件,一半不是你写的”——这就是软件供应链安全的现实。一次上游投毒、一次依赖劫持,能让整个组织的基础设施瞬间失守。本文按防御链展开:SBOM 生成(Syft/CycloneDX)→ 漏洞审计(Grype/osv-scanner)→ 产物签名(cosign)→ 依赖与锁文件治理 → SCA 进流水线门禁 → SLSA 零信任发布 → CVE 应急响应,每步都给出现成命令与配置。
目录
- 1. 供应链攻击形态与威胁模型
- 2. SBOM 生成:Syft 与 CycloneDX
- 3. SBOM 审计与漏洞匹配:Grype
- 4. 镜像与产物签名:cosign 与 Notary
- 5. 依赖策略与锁文件治理
- 6. SCA 扫描进流水线
- 7. 零信任发布与可溯源
- 8. 合规与应急响应
- 9. 案例与最佳实践
1. 供应链攻击形态与威胁模型
1.1 三类主要攻击形态
上游投毒:往开源依赖/基础镜像植入恶意代码(event-stream、ua-parser-js)
构建链污染:攻破 CI 或镜像仓库替换产物(SolarWinds 类)
依赖劫持:typosquatting(近似包名)、同名抢注、版本回滚攻击
1.2 攻击路径示例
恶意 npm 包(名字像合法包)→ 开发者安装 → CI 构建时执行窃密脚本 →
窃取部署凭证 → 提权到镜像仓库/云账号。攻击往往发生在"没人检查"的构建环节
1.3 信任链与威胁模型
| 信任点 | 攻击方式 | 防御手段 |
|---|---|---|
| 上游源码 | 提交被篡改/维护者失陷 | 签名校验 + 关注维护者动态 |
| 依赖包 | 投毒/劫持 | 锁文件 + 哈希校验 + SCA |
| 构建环境 | 凭证窃取/替换产物 | 隔离 Runner + 短时令牌 |
| 镜像仓库 | 未授权写入 | cosign 签名 + 仓库 RBAC |
| 部署环境 | 运行被替换镜像 | 准入控制验证签名 |
2. SBOM 生成:Syft 与 CycloneDX
2.1 SBOM 是什么
SBOM(软件物料清单)= 机器可读的组件清单,回答"这个产物里有什么"
标准格式:SPDX 与 CycloneDX;用途:出漏洞定位受影响镜像、合规审计、供应链透明
2.2 用 Syft 生成 SBOM
# 扫镜像生成 CycloneDX SBOM
syft packages registry:5000/shop/api:v1.2.3 -o cyclonedx-json > sbom.api.json
# 扫构建目录(未打包前)
syft packages dir:dist/ -o spdx-json > sbom.spdx.json
# 校验 SBOM 语法(防格式错误导致审计工具拒读)
cyclonedx validate --input-file sbom.api.json
2.3 SBOM 随制品保存
SBOM 要随产物归档:镜像 → cosign attach sbom 写入 Registry;包 → 进制品库;无 SBOM = "成分未知,禁止上线"
3. SBOM 审计与漏洞匹配:Grype
3.1 Grype 匹配漏洞
# 直接扫镜像(内部先生成 SBOM 再匹配漏洞库)
grype registry:5000/shop/api:v1.2.3 --only-fixed --fail-on high
# 扫已有 SBOM 文件(审计外部给的清单)
grype sbom:sbom.api.json -o json > grype-report.json
3.2 关注"新引入漏洞"而非总数
全量漏洞数是存量负债,真正危险的是"本次新引入的漏洞":
以基线(上周扫描结果)做 diff,新出现 CRITICAL/HIGH 直接拦截构建;
存量漏洞按修复计划 + 风险接受管理,不阻塞一切发布
基线 diff:osv-scanner --format json -r . > current.json,对比上一版本
3.3 Google OSV 与全语言覆盖
# osv-scanner:统一用 OSV 数据库覆盖 npm/pypi/go/maven/rust
osv-scanner -r --lockfile-locations package-lock.json,poetry.lock,go.sum
# 优点:CVE 之外还能发现 GitHub 安全公告、恶意包(malicious)条目
4. 镜像与产物签名:cosign 与 Notary
4.1 cosign 签名
# 生成密钥对(或用 KMS 保管私钥)
cosign generate-key-pair
# 给镜像签名(签名作为 OCI Artifact 存进仓库)
cosign sign --key cosign.key registry:5000/shop/api:v1.2.3
# 验证签名(部署/拉取前)
cosign verify --key cosign.pub registry:5000/shop/api:v1.2.3
4.2 签名 + SBOM 一起附加
# 把 SBOM 作为 attestation 附加到镜像
cosign attach sbom --sbom sbom.api.json \
registry:5000/shop/api:v1.2.3
# 校验镜像摘要(防仓库被改写后签名仍匹配)
cosign verify-attestation --key cosign.pub \
registry:5000/shop/api@sha256:...
4.3 部署时验证签名
签名必须在"部署时验证"才有效:K8s 用 Kyverno/cosign 准入控制器,强制镜像
带有效签名才允许创建 Pod;CI 发布前强制 verify,不通过即发布失败
Notary(Docker 早期方案)已基本被 cosign 取代,新项目优先 cosign
5. 依赖策略与锁文件治理
5.1 锁文件是底线
锁文件 = 记录精确版本 + 哈希,防止"昨天构建和今天构建不一样"
npm → package-lock.json / Python → poetry.lock / Go → go.sum / Java → gradle.lockfile
门禁:仓库必须提交锁文件,CI 构建若产生锁文件 diff → 拦截
5.2 依赖引入策略
最小依赖:新引入需说明理由,控制依赖树深度
固定版本:禁用浮动版本(^1.2.3、latest),只用精确版本
许可合规:许可证纳入审查(Copyleft 需评估);只用官方源 + 校验哈希
5.3 自动升级与人工审批
# Renovate:patch 自动合入;major 人工审;升级 PR 必须先过 SCA 门禁
{
"extends": ["config:recommended"],
"automerge": true,
"packageRules": [
{ "matchUpdateTypes": ["major"], "automerge": false },
{ "matchUpdateTypes": ["patch"], "automerge": true }
]
}
6. SCA 扫描进流水线
6.1 SCA 工具对比
| 工具 | 覆盖 | 数据源 | 特点 |
|---|---|---|---|
| Trivy | 镜像+SBOM+依赖 | 多源 | Go 单二进制,CI 友好 |
| Grype | SBOM+镜像 | 多源 | 与 Syft 同生态 |
| osv-scanner | 锁文件 | OSV | 含恶意包告警 |
| Snyk | 依赖+IaC | 商业库 | 覆盖全但付费 |
6.2 扫描进流水线门禁
# GitHub Actions:供应链扫描步骤
steps:
- uses: actions/checkout@v4
- name: SBOM
run: syft dir:. -o cyclonedx-json > sbom.json
- name: Scan
run: grype sbom:sbom.json --fail-on high --only-fixed
- name: Secret guard
run: gitleaks detect --exit-code 1
- name: Gate
run: |
python ci/supply_gate.py --sbom sbom.json \
--max-new-critical 0 --max-new-high 0
6.3 门禁分级
CRITICAL 新引入 → 拦截;HIGH 新引入 → 拦截;中危/存量 → 台账按 SLA 修复
恶意包(OSV malicious)→ 无条件拦截 + 排查是否已被使用
7. 零信任发布与可溯源
7.1 零信任发布四要素
身份可信:构建者身份可验证;产物可信:镜像带签名部署时验证
成分可信:SBOM 完整且无高危漏洞;过程可信:构建过程可审计(谁/何时/何代码)
7.2 SLSA 等级实践
SLSA 由低到高 L1~L3:L1 构建有文档+产物带哈希;L2 托管构建+签名+防篡改;
L3 不可伪造来源证明(provenance)+隔离构建
多数组织目标 L2:托管 CI + cosign 签名 + provenance 记录
7.3 来源证明(Provenance)
# 记录"这段镜像由哪个仓库哪个 commit 构建"(概念)
cosign attest --predicate provenance.json --key cosign.key \
registry:5000/shop/api:v1.2.3
# 事后审计:verify-attestation 拉出 provenance 核对 commit 与构建者
8. 合规与应急响应
8.1 合规要求
典型要求:随时提供每个产物的 SBOM、证明产物来源与签名者
(美国 EO 14028、欧盟 CRA、各地关基要求)→ SBOM 归档 + 签名留痕
8.2 CVE 应急响应流程
重大漏洞公布(如某日志库 RCE):
1. SBOM 全量匹配受影响产物 → 2. 按暴露面排序(公网 > 内网 > 离线)
3. 升级依赖 → 重建 → 过门禁 → 发布 → 4. 验证回写台账
无法升级的组件做缓解措施(WAF/禁用功能)
8.3 演练与度量
每季度"模拟 log4j"演练:公布假 CVE,考核定位/修复发布时长与沟通顺畅度
度量指标:MTTD(发现时长)、MTTR(修复时长)、新漏洞拦截率
9. 案例与最佳实践
9.1 真实案例
某中型电商(概念案例):某 npm 依赖爆出 RCE,两天找不到用了哪些镜像
改造:syft 生成 SBOM + cosign 签名归档 → Grype 扫 SBOM 进门禁 → 验签准入
第二次 CVE:10 分钟筛出 3 个受影响镜像,2 小时完成修复发布
9.2 最佳实践 Checklist
□ 每个可发布产物强制生成 SBOM(Syft/CycloneDX)并归档
□ Grype/osv-scanner 扫 SBOM,新引入高危漏洞拦截
□ 镜像/包全部 cosign 签名,部署时准入验证签名
□ 签名与 SBOM 作为 attestation 随镜像进 Registry;仓库强制锁文件
□ Renovate/Dependabot 自动升级 + 人工审批 major
□ SCA 扫描进流水线门禁,恶意包无条件拦截
□ 构建过程记录 provenance(SLSA L2 起步)
□ 建立 CVE 应急响应流程,季度演练
9.3 常见坑
| 坑 | 现象 | 对策 |
|---|---|---|
| SBOM 生成不归档 | 应急时找不到 | 随产物进制品库 |
| 只扫不签名 | 仓库被改无法发现 | cosign 签名 + 部署验签 |
| 全量漏洞当门禁 | 存量债阻塞所有发布 | 区分新引入 vs 存量 |
| 锁文件不入库 | 构建结果漂移 | CI 强制锁文件 diff 检查 |
| 签名只在 CI 验 | 部署环节被绕过 | 准入控制器部署时验签 |
| 只防 npm | Java/Go/镜像漏掉 | 全语言 osv-scanner 覆盖 |
小结
供应链安全 = SBOM 生成(Syft)→ 漏洞审计(Grype/osv-scanner)→ 产物签名(cosign)→ 依赖锁文件治理 → SCA 流水线门禁 → SLSA 零信任发布 → CVE 应急响应。核心三件事:知道产物里有什么(SBOM)、验证产物是谁造的(签名)、阻止坏成分进线(门禁)。从"构建时生成 SBOM + 签名 + 新漏洞拦截"这条最小链起步,再逐步补全 provenance 与应急演练,就能把供应链从"黑箱"变成"可审计的清单"。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。