Analyzing SBOM for Supply Chain Vulnerabilities
使用すべき状況
- 新しい規制要件(EO 14028、EU 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)へのネットワークアクセス
- オプション:SBOM生成用のsyft、クロスバリデーション用の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はnpm、PyPI、Maven、Goモジュール、apt、apk、RPMを含む30以上のパッケージエコシステムをサポートします。生成されたSBOMには、パッケージ名、バージョン、ライセンス、CPE識別子、PURL(Package 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キーなしで5リクエスト/30秒、キーありで50リクエスト/30秒。
ステップ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レベル深い)の脆弱性もリスクを表しますが、修正が難しい場合があります。
リスク評価のための主要なグラフ指標:
- 入次数(In-degree):このコンポーネントに依存するコンポーネントの数(高入次数 = 広い影響範囲)
- ルートへの最短パス:アプリケーションエントリポイントからの距離(近いほど悪用されやすい)
- 媒介中心性(Betweenness centrality):多くの依存パス上に位置するコンポーネント(ボトルネックリスク)
ステップ5:リスクスコアの計算
脆弱性データをコンポーネントおよび全体のリスクスコアに集約します:
リスクスコア計算:
━━━━━━━━━━━━━━━━━━━━━━━━━━
コンポーネントリスク = コンポーネントに影響する全CVEのCVSSスコアの最大値
加重リスク = コンポーネントリスク × 依存係数
ここで、依存係数 = 1.0 + (0.1 × 入次数)
(依存数が多いほど組織への影響が大きい)
SBOM全体のリスク = 全コンポーネントリスクの加重平均
依存関係の中心性で重み付け
リスクレベル:
CRITICAL: CVSS >= 9.0 または既知の悪用(CISA KEV)
HIGH: CVSS >= 7.0
MEDIUM: CVSS >= 4.0
LOW: CVSS < 4.0
ステップ6:Grypeによるクロスバリデーション
grypeを使用してSBOMを独立してスキャンし、結果を比較します:
# CycloneDX SBOMをgrypeでスキャン
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 Security Advisories、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)
脆弱性サマリー
Critical: 3 コンポーネント / 5 CVE
High: 11 コンポーネント / 18 CVE
Medium: 27 コンポーネント / 41 CVE
Low: 8 コンポーネント / 12 CVE
Critical 検出結果
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レベル)
ボトルネックコンポーネント: 依存パスの50%以上に存在する3つのコンポーネント
ライセンスコンプライアンス
Copyleftライセンス発見: 2 (libxml2のGPL-3.0、mongodb-driverのAGPL-3.0)
商用配布にはレビューが必要
主要概念
| 用語 | 定義 |
|---|---|
| SBOM | ソフトウェア部品表。ソフトウェア製品内の全コンポーネント、ライブラリ、依存関係の正式な棚卸し |
| CycloneDX | OWASPが維持するSBOM標準。JSON、XML、protobuf形式をサポートし、依存関係グラフと脆弱性データを含む |
| SPDX | Linux FoundationのSBOM標準。ライセンスコンプライアンスに重点を置き、パッケージ、ファイル、スニペットレベルの詳細をサポート |
| PURL | Package URL。エコシステム全体でソフトウェアパッケージを識別する標準化されたスキーム(例:pkg:npm/[email protected]) |
| CPE | 共通プラットフォーム列挙。IT製品向けのNIST命名スキームで、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に依存しているかを特定
- 影響範囲の計算:露出しているサービスとエンドポイントの数
- 露出とビジネス重要度で優先順位付けされた修正レポートを生成
- 同じSBOMに対するgrypeスキャンで結果をクロスバリデーション
注意点:
- ベンダーSBOMは不完全である可能性があり、log4jを埋め込んだshaded/bundled JARファイルを見逃す可能性がある
- SPDXとCycloneDXのバージョン違いがパーサーの互換性に影響する可能性がある
- NVD APIのレート制限により、APIキーなしで数百のコンポーネントをスキャンする際に分析が遅くなる可能性がある
- SBOM内のCPE名がNVDエントリと完全に一致しない場合があり、ファジーマッチングが必要になる
- 推移的依存関係には、直接依存関係でなくてもlog4jが含まれる可能性がある