SBOM供应链漏洞分析 是做什么的?
何时使用
- 新的合规要求(如EO 14028、欧盟CRA)要求在软件交付时进行SBOM分析
- 安全团队需要通过扫描供应商提供的SBOM来评估第三方风险
- CI/CD流水线需要对生成的SBOM进行自动化漏洞检查
- 事件响应团队需要确定新披露的CVE是否影响已部署的软件
- 采购团队需要对软件采购进行供应链风险评估
请勿用于对运行中系统的运行时漏洞扫描;应使用容器扫描工具(Trivy、Grype CLI)或主机级漏洞扫描器(Nessus、Qualys)。
前置条件
- CycloneDX JSON (v1.4+) 或 SPDX JSON (v2.3+) 格式的SBOM文件
- Python 3.9+,并安装requests、networkx和packaging库
- NVD API密钥(免费,从 https://nvd.nist.gov/developers/request-an-api-key 获取),用于更高的速率限制
- 能够访问NVD API(https://services.nvd.nist.gov/rest/json/cves/2.0)
- 可选:syft用于生成SBOM,grype用于交叉验证
工作流程
步骤1:生成SBOM(如果未提供)
使用syft从容器镜像或项目目录创建SBOM:
# 从容器镜像生成CycloneDX JSON
syft alpine:latest -o cyclonedx-json > sbom-cyclonedx.json
# 从项目目录生成SPDX JSON
syft dir:/path/to/project -o spdx-json > sbom-spdx.json
# 从正在运行的容器生成
syft docker:my-app-container -o cyclonedx-json > sbom.json
Syft支持超过30种软件包生态系统,包括npm、PyPI、Maven、Go模块、apt、apk和RPM。生成的SBOM包含软件包名称、版本、许可证、CPE标识符和PURL(包URL)引用。
步骤2:解析SBOM并提取组件
解析SBOM以提取所有软件组件及其标识符:
CycloneDX JSON结构:
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"components": [
{
"type": "library",
"name": "lodash",
"version": "4.17.20",
"purl": "pkg:npm/[email protected]",
"cpe": "cpe:2.3:a:lodash:lodash:4.17.20:*:*:*:*:*:*:*",
"licenses": [{"license": {"id": "MIT"}}]
}
],
"dependencies": [
{"ref": "pkg:npm/[email protected]", "dependsOn": ["pkg:npm/[email protected]"]}
]
}
SPDX JSON结构:
{
"spdxVersion": "SPDX-2.3",
"packages": [
{
"name": "lodash",
"versionInfo": "4.17.20",
"externalRefs": [
{"referenceType": "purl", "referenceLocator": "pkg:npm/[email protected]"},
{"referenceType": "cpe23Type", "referenceLocator": "cpe:2.3:a:lodash:lodash:4.17.20:*:*:*:*:*:*:*"}
],
"licenseConcluded": "MIT"
}
],
"relationships": [
{"spdxElementId": "SPDXRef-express", "relatedSpdxElement": "SPDXRef-lodash",
"relationshipType": "DEPENDS_ON"}
]
}
步骤3:将组件与NVD CVE数据库关联
查询NVD 2.0 API以查找每个组件的已知漏洞:
import requests
NVD_API = "https://services.nvd.nist.gov/rest/json/cves/2.0"
def search_cves_by_cpe(cpe_name, api_key=None):
params = {"cpeName": cpe_name, "resultsPerPage": 50}
headers = {"apiKey": api_key} if api_key else {}
resp = requests.get(NVD_API, params=params, headers=headers, timeout=30)
resp.raise_for_status()
return resp.json().get("vulnerabilities", [])
def search_cves_by_keyword(keyword, version=None, api_key=None):
params = {"keywordSearch": keyword, "resultsPerPage": 50}
headers = {"apiKey": api_key} if api_key else {}
resp = requests.get(NVD_API, params=params, headers=headers, timeout=30)
resp.raise_for_status()
return resp.json().get("vulnerabilities", [])
NVD API支持按CPE名称(最精确)、关键字、CVE ID和日期范围进行搜索。速率限制:无API密钥时为每30秒5个请求,有密钥时为每30秒50个请求。
步骤4:构建依赖关系图并识别传递性风险
构建有向依赖关系图以追踪漏洞传播:
import networkx as nx
def build_dependency_graph(sbom):
G = nx.DiGraph()
# 为每个组件添加节点
for comp in sbom["components"]:
G.add_node(comp["purl"], name=comp["name"], version=comp["version"])
# 从依赖关系中添加边
for dep in sbom.get("dependencies", []):
for child in dep.get("dependsOn", []):
G.add_edge(dep["ref"], child)
return G
传递性依赖分析可以识别出那些未直接包含,但通过依赖链引入的组件。一个深度嵌套的传递性依赖(例如,4层深度)中的漏洞仍然代表风险,但修复起来可能更加困难。
用于风险评估的关键图指标:
- 入度:有多少组件依赖于此组件(入度高表示影响范围广)
- 到根节点的最短路径:与应用程序入口点的距离(越近越容易被利用)
- 中介中心性:位于许多依赖路径上的组件(瓶颈风险)
步骤5:计算风险评分
将漏洞数据汇总为组件和整体风险评分:
风险评分计算:
━━━━━━━━━━━━━━━━━━━━━━
组件风险 = 影响该组件的所有CVE的CVSS分数的最大值
加权风险 = 组件风险 * 依赖因子
其中依赖因子 = 1.0 + (0.1 * 入度)
(依赖者越多,组织影响越大)
整体SBOM风险 = 所有组件风险的加权平均值
权重由依赖中心性决定
风险等级:
严重: CVSS >= 9.0 或已知被利用(CISA KEV)
高: CVSS >= 7.0
中: CVSS >= 4.0
低: CVSS < 4.0
步骤6:使用Grype交叉验证
使用grype独立扫描SBOM并比较结果:
# 使用grype扫描CycloneDX SBOM
grype sbom:sbom-cyclonedx.json -o json > grype-results.json
# 扫描SPDX SBOM
grype sbom:sbom-spdx.json -o table
# 按严重程度过滤
grype sbom:sbom-cyclonedx.json --only-fixed --fail-on critical
Grype从NVD、GitHub安全公告、Alpine SecDB、Red Hat、Debian、Ubuntu、Amazon Linux和Oracle安全数据库中获取漏洞数据,覆盖范围比仅使用NVD更广。
步骤7:生成合规报告
生成适合监管合规的结构化报告:
SBOM 漏洞分析报告
====================================
SBOM文件: app-sbom-cyclonedx.json
格式: CycloneDX v1.5
分析日期: 2026-03-19
组件总数: 247
依赖总数: 1,842(直接:34,传递:213)
漏洞摘要
严重: 3个组件 / 5个CVE
高: 11个组件 / 18个CVE
中: 27个组件 / 41个CVE
低: 8个组件 / 12个CVE
严重发现
1. [email protected]
CVE-2021-23337 (CVSS 7.2) - 模板中的命令注入
CVE-2020-28500 (CVSS 5.3) - trimEnd中的ReDoS
依赖者:14个组件(影响范围广)
修复:升级到4.17.21+
2. [email protected]
CVE-2021-44228 (CVSS 10.0) - Log4Shell RCE [CISA KEV]
CVE-2021-45046 (CVSS 9.0) - 不完整修复绕过
依赖者:8个组件
修复:升级到2.17.1+
依赖图风险
依赖最多的组件:[email protected](47个依赖者)
最深层链:app -> framework -> adapter -> codec -> zlib(5层)
瓶颈组件:3个组件位于超过50%的依赖路径上
许可证合规性
发现的Copyleft许可证:2个(libxml2中的GPL-3.0,mongodb-driver中的AGPL-3.0)
商业分发前需要审查
关键概念
| 术语 | 定义 |
|---|---|
| SBOM | 软件物料清单;软件产品中所有组件、库和依赖项的正式清单 |
| CycloneDX | OWASP维护的SBOM标准,支持JSON、XML和protobuf格式,包含依赖图及漏洞数据 |
| SPDX | Linux Foundation的SBOM标准,侧重于许可证合规性,支持包、文件和片段级别详情 |
| PURL | 包URL;跨生态系统识别软件包的标准化方案(例如pkg:npm/[email protected]) |
| CPE | 通用平台枚举;NIST的IT产品命名方案,用于与NVD CVE数据关联 |
| NVD | 国家漏洞数据库;美国政府的漏洞数据库,通过CVE标识符索引 |
| 传递性依赖 | 未直接声明,但通过直接依赖的依赖链引入的依赖 |
| CISA KEV | CISA已知被利用漏洞目录;确认已在野外被积极利用的CVE |
工具与系统
- syft (Anchore):开源SBOM生成器,支持30多种软件包生态系统和CycloneDX/SPDX输出
- grype (Anchore):漏洞扫描器,接受SBOM作为输入,并关联多个咨询数据库
- cyclonedx-python-lib:用于以编程方式创建、解析和验证CycloneDX SBOM的Python库
- lib4sbom:用于解析SPDX和CycloneDX格式SBOM的Python库
- nvdlib:NVD 2.0 API的Python封装,支持CVE和CPE查询并管理速率限制
- OWASP Dependency-Track:用于持续SBOM分析、漏洞追踪和策略执行的平台
常见场景
场景:Log4Shell披露后评估供应商软件
背景:在Log4Shell(CVE-2021-44228)披露后,安全团队需要确定哪些供应商提供的应用程序包含易受攻击的log4j版本。根据合同要求,多家供应商已提供SBOM。
方法:
- 收集所有供应商的SBOM(CycloneDX或SPDX JSON格式)
- 解析每个SBOM,搜索版本低于2.17.1的log4j-core组件
- 查询NVD API以获取特定的CVE(CVE-2021-44228、CVE-2021-45046、CVE-2021-45105)
- 构建依赖关系图,确定哪些应用程序组件依赖于log4j
- 计算影响范围:有多少服务和端点暴露
- 生成按暴露程度和业务重要性排序的优先修复报告
- 使用grype扫描相同的SBOM进行交叉验证
陷阱:
- 供应商提供的SBOM可能不完整,可能遗漏了嵌入了log4j的阴影/捆绑JAR文件
- SPDX和CycloneDX版本差异可能影响解析器兼容性
- 在没有API密钥的情况下扫描数百个组件时,NVD API速率限制可能减慢分析速度
- SBOM中的CPE名称可能与NVD条目不完全匹配,需要进行模糊匹配
- 即使log4j不是直接依赖项,传递性依赖也可能包含它