SBOM 软件物料清单
Log4Shell 漏洞爆发后,某公司的安全团队面临一个噩梦般的问题:他们的应用依赖了 Log4j,但没人知道具体哪个应用依赖了它、哪个版本、是否在实际使用。
他们花了三天时间排查所有应用。这三天里,系统在生产环境中运行着有漏洞的组件。
如果他们有 SBOM,这个过程可能只需要几分钟。
SBOM 的定义与价值
SBOM(Software Bill of Materials,软件物料清单)是一种标准化文档,记录了软件产品的所有组件、依赖及其关系。
为什么需要 SBOM
漏洞响应:当新漏洞披露时,快速定位受影响的应用。
许可证合规:了解所有组件的许可证,避免法律风险。
供应链透明:了解依赖关系,识别供应链风险。
合规要求:满足 NTIA、EU Cyber Resilience Act 等法规要求。
SBOM 的价值矩阵
SBOM 的格式
SPDX
SPDX(Software Package Data Exchange)是 Linux 基金会支持的 SBOM 标准格式。
SPDX
SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: myapp-sbom
DocumentNamespace: https://example.com/myapp/sbom
Creator: Tool: syft@v0.80.0
Created: 2024-01-15T10:00:00Z
PackageName: myapp
PackageVersion: 1.0.0
PackageDownloadLocation: https://myregistry.com/myapp:v1.0
FilesAnalyzed: false
PRIVATE: false
PackageSupplier: Organization: Example Inc.
PackageLicenseDeclared: Apache-2.0
PackageLicenseConcluded: Apache-2.0
ExternalRef: SECURITY-cpe_vulnerability: cpe:2.3:a:apache:log4j:2.17.0
ExternalRef: SECURITY-cwe: CWE-502
CycloneDX
CycloneDX 是 OWASP 支持的 SBOM 格式,更适合现代应用。
CycloneDX
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"metadata": {
"timestamp": "2024-01-15T10:00:00Z",
"tools": [
{
"name": "syft",
"version": "0.80.0"
}
],
"component": {
"name": "myapp",
"version": "1.0.0",
"type": "application"
}
},
"components": [
{
"type": "library",
"name": "log4j-core",
"version": "2.17.0",
"group": "org.apache.logging.log4j",
"purl": "pkg:maven/org.apache.logging.log4j/log4j-core@2.17.0",
"licenses": [{"license": {"id": "Apache-2.0"}}],
"vulnerabilities": [
{
"id": "CVE-2021-44228",
"severity": "critical",
"cvss": {"score": 10.0}
}
]
}
]
}
SWID
SWID(Software Identification)是 ISO/IEC 19770-2 标准定义的标签格式。
SWID
<?xml version="1.0" encoding="UTF-8"?>
<SoftwareIdentity
xmlns="http://standards.iso.org/19770-2/2015/schema.xsd"
name="myapp"
version="1.0.0"
tagId="example.com-myapp-1.0.0">
<Entity name="Example Inc." role="manufacturer"/>
<Link href="https://myregistry.com/myapp:v1.0" rel="installationmedia"/>
<Component name="log4j-core" version="2.17.0"/>
</SoftwareIdentity>
格式对比
SBOM 的生成方法
Syft
Syft 是最流行的 SBOM 生成工具,支持多种格式。
Syft
# 安装 Syft
brew install syft
# 扫描容器镜像
syft myregistry.com/myapp:v1.0
# 输出 CycloneDX JSON
syft myregistry.com/myapp:v1.0 -o cyclonedx-json > sbom.json
# 输出 SPDX
syft myregistry.com/myapp:v1.0 -o spdx-json > sbom.spdx
# 扫描文件系统
syft dir:/path/to/project -o cyclonedx-json > sbom.json
Trivy
Trivy 可以同时生成镜像扫描报告和 SBOM。
Trivy
# 生成 SBOM
trivy image --format cyclonedx myregistry.com/myapp:v1.0 > sbom.json
# 扫描 SBOM 中的漏洞
trivy sbom sbom.json
CI/CD 集成
GitHub
name: Generate SBOM
on:
push:
tags:
- 'v*'
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build image
run: docker build -t ${{ env.IMAGE_NAME }}:${{ github.ref_name }} .
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ${{ env.IMAGE_NAME }}:${{ github.ref_name }}
format: spdx-json
output-file: sbom.spdx.json
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.spdx.json
SBOM 与 CVE 漏洞管理
漏洞关联分析
SBOM
{
"vulnerabilities": [
{
"id": "CVE-2021-44228",
"source": "NVD",
"severity": "critical",
"cvss": 10.0,
"affected_packages": [
"log4j-core@2.14.0",
"log4j-core@2.15.0"
],
"fixed_in": [
"log4j-core@2.17.0"
]
}
]
}
Java 漏洞管理示例
SBOM
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class SbomVulnerabilityService {
private final ObjectMapper mapper = new ObjectMapper();
public List<Vulnerability> findVulnerabilities(File sbomFile, String cveId)
throws IOException {
JsonNode sbom = mapper.readTree(sbomFile);
List<Vulnerability> results = new ArrayList<>();
JsonNode components = sbom.get("components");
for (JsonNode component : components) {
if (component.has("vulnerabilities")) {
for (JsonNode vuln : component.get("vulnerabilities")) {
if (cveId.equals(vuln.get("id").asText())) {
results.add(new Vulnerability(
component.get("name").asText(),
component.get("version").asText(),
vuln
));
}
}
}
}
return results;
}
public record Vulnerability(
String component,
String version,
JsonNode details
) {}
}
SBOM 与许可证合规
许可证冲突检测
许可证检查
# Syft 许可证分析
syft myregistry.com/myapp:v1.0 -o table
# 输出示例
NAME VERSION TYPE LICENSES
-------------------------- -------------- ---------- -----------
log4j-core 2.17.0 maven Apache-2.0
spring-boot 3.2.0 maven Apache-2.0
mysql-connector 8.0.33 maven GPL-2.0
commons-codec 1.15 maven Apache-2.0
许可证合规工作流
flowchart TB
A[SBOM 生成] --> B[许可证解析]
B --> C{许可证冲突?}
C -->|是| D[法务审查]
C -->|否| E[通过]
D --> F[风险评估]
F --> G{可接受?}
G -->|否| H[替换组件]
G -->|是| E
H --> A
SBOM 的消费与使用
安全扫描集成
使用
# 基于 SBOM 扫描
trivy sbom path/to/sbom.json
# 扫描特定 CVE
trivy sbom path/to/sbom.json --vuln-id CVE-2021-44228
# 输出 JSON 报告
trivy sbom path/to/sbom.json --format json --output vuln-report.json
软件组成分析
构建依赖关系图
import java.util.*;
public class DependencyGraph {
public record Dependency(
String name,
String version,
List<Dependency> children
) {}
public Dependency buildFromSbom(JsonNode sbom) {
Map<String, JsonNode> componentMap = new HashMap<>();
for (JsonNode component : sbom.get("components")) {
componentMap.put(
component.get("purl").asText(),
component
);
}
return buildDependencyTree(componentMap);
}
private Dependency buildDependencyTree(Map<String, JsonNode> components) {
// 实现依赖树的构建逻辑
return null;
}
}
SBOM 的生命周期管理
版本控制
SBOM 应该与软件版本关联,形成版本历史。
版本化
{
"metadata": {
"documentName": "myapp-sbom",
"specVersion": "1.5",
"version": 1,
"component": {
"name": "myapp",
"version": "1.0.0"
},
"build": {
"buildSystem": "GitHub Actions",
"buildUrl": "https://github.com/example/myapp/actions/runs/123456",
"builtOn": "2024-01-15T10:00:00Z"
}
}
}
存储与分发
自动化更新
定时更新
apiVersion: batch/v1
kind: CronJob
metadata:
name: sbom-refresh
namespace: devsecops
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: refresh-sbom
image: anchore/syft:latest
command:
- sh
- -c
- |
for image in $(kubectl get images -o name); do
syft $image -o cyclonedx-json > /sbom/$(echo $image | tr '/:' '-').json
done
volumeMounts:
- name: sbom-store
mountPath: /sbom
restartPolicy: OnFailure
NTIA 对 SBOM 的要求
NTIA SBOM 最小元素
2021 年 NTIA(美国国家电信和信息管理局)发布了 SBOM 最小元素要求:
NTIA 格式要求
- SPDX 或 CycloneDX ���式
- 机器可读
- 支持自动化处理
合规建议
- 自动化生成:在 CI/CD 流水线中自动生成 SBOM
- 版本对应:每个软件版本对应一个 SBOM
- 安全存储:SBOM 本身也是敏感数据,需要安全存储
- 持续更新:依赖变更时更新 SBOM
SBOM 的工具链
工具矩阵
完整工具链示例
flowchart LR
A[源代码] --> B[构建]
B --> C[Syft 生成 SBOM]
C --> D[Trivy 漏洞扫描]
D --> E[存储到仓库]
E --> F[分发 SBOM]
subgraph "CI/CD"
B -->|每次构建| C
end
subgraph "安全运营"
D -->|定期扫描| D
end
SBOM 实施建议
建议从 Syft 开始,它易于使用且支持多种输出格式。在 CI/CD 流水线中集成 SBOM 生成,确保每个构建都生成 SBOM。
总结与延伸思考
SBOM 是现代软件供应链安全的基础设施。它让组织能够回答「我们的软件��什么组成」这个关键问题。
实施建议:
- 从自动化开始:在 CI/CD 流水线中自动生成 SBOM
- 选择合适格式:CycloneDX 更适合安全工具集成
- 与漏洞管理集成:使用 SBOM 进行快速漏洞响应
- 覆盖所有组件:包括直接依赖和传递依赖
SBOM 的价值在安全事件发生时最为明显——当新的漏洞披露时,拥有 SBOM 的组织可以快速定位受影响范围,而没有 SBOM 的组织可能需要花费数天时间排查。
思考题
问题 1:为什么说 SBOM 是供应链安全的基础设施?
参考答案
SBOM 提供了软件组成的基础可见性,这是其他供应链安全工作(漏洞响应、许可证合规、SLSA)的前提。没有 SBOM,无法快速定位漏洞影响范围,无法了解许可证风险,无法验证构建完整性。SBOM 类似于制造业的 BOM(物料清单),它不是最终的安全控制,而是支持其他安全功能的基础数据。
问题 2:如何确保 SBOM 本身的真实性?
参考答案
SBOM 的真实性可以通过以下方式保证:1)签名:在 CI/CD 中使用私密密钥签名 SBOM,消费者可以验证签名;2)构建 Provenance:将 SBOM 生成过程纳入 SLSA 构建 provenance,与构建过程一起验证;3)透明日志:使用 Sigstore Rekor 等透明日志记录 SBOM 生成过程;4)来源可追溯:SBOM 应该包含生成工具、生成时间、构建系统等信息,便于审计。