ネイティブ・イメージのソフトウェア部品表(SBOM)
GraalVMネイティブ・イメージは、ビルド時にソフトウェア部品表(SBOM)を構成して、既知のセキュリティの脆弱性の影響を受ける可能性のあるライブラリを検出できます。ネイティブ・イメージには、SBOMをネイティブ実行可能ファイルに埋め込むための--enable-sbom
オプションが用意されています(Oracle GraalVMでのみ使用可能)。SBOMは、--enable-sbom=classpath,export
を使用して、埋め込む以外にクラスパスに追加することもJSONファイルとしてエクスポートすることもできます。
CycloneDX形式がサポートされており、これがデフォルトです。CycloneDX SBOMをネイティブ実行可能ファイルに埋め込むには、--enable-sbom
オプションをnative-image
コマンドに渡します。
この実装では、ネイティブ実行可能ファイルに含まれるクラスについて、外部ライブラリ・マニフェストで監視可能なすべてのバージョン情報をリカバリすることによってSBOMが構築されます。また、SBOMは、ネイティブ実行可能ファイル・サイズへの影響を抑えるために圧縮されます。SBOMはgzip
形式で格納され、エクスポートされたsbom
シンボルはその開始アドレスを参照し、sbom_length
シンボルはそのサイズを参照します。
SBOMの内容の抽出
圧縮されたSBOMをイメージに埋め込んだ後、SBOMの内容を抽出するには次の2つの方法があります:
- ネイティブ・イメージ検査ツールの使用
- Syftの使用
ネイティブ・イメージ検査ツール
ネイティブ・イメージ検査ツールでは、--sbom
パラメータを使用して圧縮されたSBOMを抽出でき、実行可能ファイルと共有ライブラリの両方からアクセスできます:
native-image-inspect --sbom <path_to_binary>
内容がJSON形式で出力されます:
{
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"version": 1,
"components": [
{
"bom-ref": "pkg:maven/io.netty/netty-codec-http2@4.1.104.Final",
"type": "library",
"group": "io.netty",
"name": "netty-codec-http2",
"version": "4.1.104.Final",
"purl": "pkg:maven/io.netty/netty-codec-http2@4.1.104.Final",
"properties": [
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:codec:codec:4.1.76.Final:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:codec:netty-codec-http2:4.1.76.Final:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:codec:netty_codec_http2:4.1.76.Final:*:*:*:*:*:*:*"
},
...
]
},
...
],
"dependencies": [
{
"ref": "pkg:maven/io.netty/netty-codec-http2@4.1.104.Final",
"dependsOn": [
"pkg:maven/io.netty/netty-buffer@4.1.104.Final",
"pkg:maven/io.netty/netty-codec-http@4.1.104.Final",
"pkg:maven/io.netty/netty-codec@4.1.104.Final",
"pkg:maven/io.netty/netty-common@4.1.104.Final",
"pkg:maven/io.netty/netty-transport@4.1.104.Final"
]
},
...
],
"serialNumber": "urn:uuid:51ec305f-616e-4139-a033-a094bb94a17c"
}
Syft
Syftは、コンテナ・イメージおよびファイルシステム用のSBOMを生成するAnchoreによって開発されたオープンソース・ツールです。さらに、埋込みSBOMを抽出し、ネイティブのSyft形式とCycloneDX形式の両方で表示できます。GraalVMチームの貢献により、syft
は、Linux、macOSまたはWindows用にビルドされたネイティブ・イメージ内から埋込みSBOMを抽出できます。
ネイティブ実行可能ファイルに対してsyft scan
を実行して、SBOMの内容全体を抽出します:
syft scan <path_to_binary> -o cyclonedx-json
含まれているJavaライブラリのみをリストするには、次を実行します:
syft <path_to_binary>
次のようなリストが出力されます:
NAME VERSION TYPE
Oracle GraalVM 25+12-LTS graalvm-native-image
collections 25+12-LTS java-archive
commons-validator 1.9.0 java-archive
json 20211205 java-archive
...
セキュリティ・スキャンの有効化
生成されたSBOMを利用して、セキュリティ・スキャン・ソリューションと統合できます。アプリケーションの依存関係におけるセキュリティの脆弱性を検出および軽減するのに役立つ様々なツールがあります。
1つの例は、Oracleのアプリケーション依存関係管理(ADM)です。SBOMをADM脆弱性スキャナに送信すると、アプリケーションの依存関係が識別され、既知のセキュリティ脆弱性を含むものにフラグが付けられます。ADMは、National Vulnerability Database(NVD)を含むコミュニティ・ソースからの脆弱性レポートに依存しています。また、GitHubアクション、GitLabおよびJenkinsパイプラインとも統合されます。
もう1つの一般的なコマンドライン・スキャナは、Anchoreソフトウェア・サプライ・チェーン管理プラットフォームの一部であるgrype
です。grype
を使用すると、SBOMにリストされているライブラリに、Anchoreのデータベースに記載されている既知の脆弱性があるかどうかを確認できます。native-image-inspect
ツールの出力を次のコマンドを使用してgrype
に直接入力し、脆弱なライブラリをスキャンできます:
native-image-inspect --sbom <path_to_binary> | grype
これによって、次の出力が生成されます。
NAME INSTALLED VULNERABILITY SEVERITY
netty-codec-http2 4.1.76.Final CVE-2022-24823 Medium
生成されたレポートを使用して、実行可能ファイル内の脆弱な依存関係を更新できます。
自動スキャン
CI/CDワークフローへのセキュリティ・スキャンの統合がかつてないほど簡単になりました。GraalVM GitHubアクションでSBOMサポートを使用できるため、生成されたSBOMはGitHubの依存関係送信APIを使用して自動的に送信および分析できます。次のことが可能です:
- GitHubのDependabotによる脆弱性追跡。
- GitHubの依存関係グラフによる依存関係追跡。
この統合により、開発ライフサイクル全体を通じてアプリケーションの脆弱性を継続的にモニターできるようになります。
依存関係ツリー
SBOMは、dependencies
フィールドを介してコンポーネントの関係に関する情報を提供します。この依存関係情報は、ネイティブ・イメージの静的分析コール・グラフから導出されます。依存関係グラフを分析すると、特定のコンポーネントがアプリケーションに含まれている理由を理解するのに役立ちます。たとえば、SBOMで予期しないコンポーネントを検出すると、依存関係グラフを通じてその包含をトレースして、アプリケーションのどの部分が使用しているかを特定できます。
GraalVM GitHubアクションを使用すると、GitHubの依存関係グラフ機能にアクセスできます。
Mavenを使用したより正確なSBOM
より正確なSBOMを生成するには、GraalVMネイティブ・イメージ用のMavenプラグインの使用を検討してください。このプラグインは、SBOM作成を改善するためにネイティブ・イメージと統合されます。
プラグインは、cyclonedx-maven-plugin
を使用してベースラインSBOMを作成します。ベースラインSBOMは、コンポーネントに属するパッケージ名を定義し、ネイティブ・イメージがクラスをそれぞれのコンポーネントに関連付けるのを支援します。これは、シェーディングJARまたはファットJARが使用されている場合、native-image
ツールで困難なタスクです。この共同アプローチでは、ネイティブ・イメージは、最小限のSBOMを生成するために、コンポーネントおよび依存関係をより積極的にプルーニングすることもできます。
これらの拡張機能は、プラグイン・バージョン0.10.4以降で使用でき、--enable-sbom
オプションを使用するとデフォルトで有効になります。
SBOMへのクラス・レベルのメタデータの組込み
--enable-sbom=class-level
を使用すると、クラス・レベルのメタデータがSBOMコンポーネントに追加されます。このメタデータには、ネイティブ実行可能ファイルの一部であるJavaモジュール、クラス、インタフェース、レコード、注釈、列挙、コンストラクタ、フィールドおよびメソッドが含まれます。この情報を次のことに使用できます:
- 高度な脆弱性スキャン: 脆弱性の影響を受けるクラスまたはメソッドがCVEの一部として公開されている場合、クラスレベルのメタデータをチェックし、影響を受けるSBOMコンポーネントを含むネイティブ実行可能ファイルが実際に脆弱であるかどうかを判断できます。これにより、脆弱性スキャンの誤検知率を低減できます。
- イメージの内容の理解: クラス・レベルのメタデータをすばやく参照して検索し、ネイティブ実行可能ファイルに含まれる内容を確認します。
クラス・レベルのメタデータを含めると、SBOMのサイズが大幅に増加します。このMicronaut Hello World Restアプリケーションでは、SBOMサイズは埋込みの場合は1.1MB、エクスポートの場合は13.7MBです。クラス・レベルのメタデータのないSBOMは、埋込みの場合は3.5KB、エクスポートの場合は64KBです。SBOMが埋め込まれていないネイティブ・イメージのサイズは約52MBです。
クラス・レベルのメタデータを含めることは、Syftではサポートされていません。このメタデータを含むネストされたコンポーネント・フィールドは抽出されたSBOMから削除されるためです。この制限は、抽出されたSBOMのメタデータ表示にのみ影響します。脆弱性スキャン機能には影響しません。
データ形式
CycloneDX仕様では、親子関係を持つコンポーネントをネストすることで、階層表現を使用できます。クラス・レベルの情報をSBOMコンポーネントに埋め込むために、次のように使用されます:
[component] SBOM Component
└── [component] Java Modules
└── [component] Java Source Files
├── [property] Classes
├── [property] Interfaces
├── [property] Records
├── [property] Annotations
├── [property] Enums
├── [property] Fields
├── [property] Constructors
└── [property] Methods
各SBOMコンポーネントは、components
フィールドにそのJavaモジュールをリストします。各モジュールは名前で識別され、そのJavaソース・ファイルがcomponents
フィールドにリストされます。各ソース・ファイルは、コンポーネントのソース・ディレクトリに対する相対パスによって識別され、そのクラス、インタフェース、レコード、注釈、列挙、フィールド、コンストラクタおよびメソッドがproperties
フィールドにリストされます。
mymodule
に1つのJavaソース・ファイルを含む単純なコンポーネントの例を考えてみます:
// src/com/sbom/SBOMTestApplication.java
package com.sbom;
import org.apache.commons.validator.routines.RegexValidator;
public class SBOMTestApplication {
private static final boolean IS_EMPTY_OR_BLANK = new RegexValidator("^[\\s]*$").isValid(" ");
public static void main(String[] argv) {
System.out.println(String.valueOf(IS_EMPTY_OR_BLANK));
ClassInSameFile someClass = new ClassInSameFile("hello ", "world");
someClass.doSomething();
}
}
class ClassInSameFile {
private final String value1;
private final String value2;
ClassInSameFile(String value1, String value2) {
this.value1 = value1;
this.value2 = value2;
}
String concatenate() {
System.out.println(value1 + value2);
}
// This method is unreachable and will therefore not be included in the SBOM
String unreachable() {
return value;
}
}
クラスレベルのSBOMコンポーネントは次のようになります:
{
"type": "library",
"group": "com.sbom",
"name": "sbom-test-app",
"version": "1.0.0",
"purl": "pkg:maven/com.sbom/sbom-test-app@1.0.0",
"bom-ref": "pkg:maven/com.sbom/sbom-test-app@1.0.0",
"properties": [...],
"components": [
{
"type": "library",
"name": "mymodule",
"components": [
{
"type": "file",
"name": "com/sbom/SBOMTestApplication.java",
"properties": [
{
"name": "class",
"value": "com.sbom.ClassInSameFile"
},
{
"name": "class",
"value": "com.sbom.SBOMTestApplication"
},
{
"name": "field",
"value": "com.sbom.ClassInSameFile.value1:java.lang.String"
},
{
"name": "field",
"value": "com.sbom.ClassInSameFile.value2:java.lang.String"
},
{
"name": "field",
"value": "com.sbom.SBOMTestApplication.IS_EMPTY_OR_BLANK:boolean"
},
{
"name": "constructor",
"value": "com.sbom.ClassInSameFile(java.lang.String, java.lang.String)"
},
{
"name": "method",
"value": "com.sbom.ClassInSameFile.concatenate():java.lang.String"
},
{
"name": "method",
"value": "com.sbom.SBOMTestApplication.<clinit>():void"
},
{
"name": "method",
"value": "com.sbom.SBOMTestApplication.main(java.lang.String[]):void"
}
]
}
]
}
]
}
次の表に、クラス・レベルのメタデータの形式を示します:
種類 | CycloneDXオブジェクト | type |
name |
value |
備考 |
---|---|---|---|---|---|
モジュール | コンポーネント | library |
モジュール名 | - | 名前のないモジュールのname はunnamed module です |
ソース・ファイル | コンポーネント | file |
srcディレクトリからの相対パス | - | .java で終わり、/ で区切られ、パッケージ名から導出されたパス |
クラス | プロパティ | - | class |
完全修飾名 | 無名クラス、内部クラスおよびシール・クラスが含まれます |
インタフェース | プロパティ | - | interface |
完全修飾名 | - |
レコード | プロパティ | - | record |
完全修飾名 | - |
注釈 | プロパティ | - | annotation |
完全修飾名 | - |
フィールド | プロパティ | - | field |
className.fieldName:fieldType |
フィールド宣言 |
コンストラクタ | プロパティ | - | constructor |
className(paramType1, paramType2) |
カンマとスペースで区切ったパラメータ・タイプ |
メソッド | プロパティ | - | method |
className.methodName(paramType1, paramType2):returnType |
パラメータおよび戻り型を持つメソッド |
その他のノート:
- 配列型には
[]
という接尾辞が付きます。たとえば、文字列の配列はjava.lang.String[]
になります。 - 合成的に生成されたラムダ・クラスは含まれません。
シェーディングされたJARまたはファットJARを使用する場合、クラスレベルのメタデータがコンポーネントに正確に関連付けられないことがあります。これが発生すると、未解決のすべてのメタデータがプレースホルダ・コンポーネントに収集されます:
{
"type": "data",
"name": "class-level metadata that could not be associated with a component",
"components": [
...
]
}