CPEの命名問題を軽減する解決策 Purl

前回、[IT製品を識別するための識別子 \(CPE\) の命名問題について \#SBOM \- Qiita](https://qiita.com/ichijouji/items/1c70e546aeb30339a510)という記事で、CPEの命名問題について紹介しました。
今回は、[A Proposal to Operationalize Component Identification for Vulnerability Management](https://owasp.org/assets/files/posts/A%20Proposal%20to%20Operationalize%20Component%20Identification%20for%20Vulnerability%20Management.pdf)で提案されている、CPEの命名問題を軽減するための識別子の提案について紹介します。
本記事では、まず、命名必要となる重要な2つの原理について導入します。そして、CPEの代わりにIT製品を一意に特定する識別子として期待されている識別子について紹介します。

## 命名に必要となる重要な2つの原理
IT製品だけでなく、識別子は私たちの身の回りのものに多く使われています。例えば、マイナンバー、化学記号、音楽における音名とオクターブ番号の組合せ、物理の単位や物理量を表す記号、数式、言語です。
これらの識別子には、重要な2つの原理によって分類することができると知られています。一つは、**Intrinsic と Extrinsic**な識別子、次に**Native と Non-Native**な識別子です。
- [Intrinsic and Extrinsic identifiers – Software Heritage](https://www.softwareheritage.org/2020/07/09/intrinsic-vs-extrinsic-identifiers/)

まず、**Intrinsic と Extrinsic**な識別子とは以下の性質を持ちます：
Extrinsic:識別子とオブジェクト間の対応関係を維持するためにレジスタを使用する
Intrinsic：指定されたオブジェクトに密接に結びついており、レジスタへの登録は必要なく、標準化に関する合意のみが必要である

次に、**Native と Non-Native**な識別子は以下の性質を持ちます：

ここでは、**Intrinsic と Extrinsic**な識別子、**Native と Non-Native**な識別子とは何かを身近な例を用いて説明します。また、現在存在しているソフトウェア、ハードウェアに関する識別子がそれぞれ、どのような性質を持つかを説明します。

### Extrinsic Identifiers
Extrinsicな識別子とは、**識別子とオブジェクトの対応関係を保持するために中央レジスタを使用する**識別子のことです。
Extrinsicな識別子が何を指すかを知るためには、中央レジスタにクエリをする必要があります。

Extrinsicな識別子でみなさんに馴染みがある例はマイナンバーです。日本では、住民票を持つ日本国内の全住民に12桁の番号、マイナンバーが割り当てられます。
マイナンバー自体には、オブジェクトである人物を特定するための情報 (名前、生年月日、住所など)は含まれていません。マイナンバーは政府によって一元的に管理されており、地方公共団体情報システム機構（J-LIS）が運用を担当しています。つまり、マイナンバーと人物に対応関係があるかを知るためには、中央レジスタが必要となります。そのため、マイナンバーはExtrinsicな識別子と分類できることがわかります。

次にExtrinsicな識別子の例は、CPEです。CPEはNISTによって割り当てられ、中央レジスタに保管されます。我々があるCPEとそれに対応するソフトウェアを特定するためには、中央レジスタであるNVD内のCPEを検索する必要があります。

### Intrinsic Identifiers
対して、Intrinsicな識別子とは、**指定されたオブジェクトに密接に結びついており、中央レジスタへの登録は必要なく、標準化に関する合意のみが必要である**識別子です。識別子の情報がどのように表現されるか、標準的な合意を取ることで、識別子の作成者とユーザーという立場に関係なく、オブジェクトの知識を持つ世界中の誰もが認識できる識別子となります。そのため、**中央レジスタに依存せず、ユーザが既に持っている情報から構築できる**メリットを持っています。

Intrinsicな識別子で身近なものは化学化合物の名前です。化合物に記号を与える際、何か中央レジスタへ登録し、管理する必要はありません。化学記号は標準的な命名法[^1]を学べば、世界中の人が理解できる識別子です。NaClと書けば、誰がどう見ても、どの言語で話していても、何か特別な中央レジスタに依存せず、それが塩化ナトリウムであるとわかります。
これに関連して、物理量や数式なども特別な例を除いて、Intrinsicな識別子であると言えます。$H$と書けば、エネルギーに対応する物理量ハミルトニアンですし、$f(x)$と書けば、変数$x$を引数とする関数$f$であるとわかります。

## native and non-native
次に重要な原理は**native、non-nativeな識別子**です。
nativeな識別子とは、**既存のエコシステムですでに使用されており、そのエコシステム内の人々にとって馴染みのある識別子**です。
nativeな識別子の例として、化合物名が挙げられています。化合物名は少なくとも200年間使用されており、すべての化学者にとって馴染みのある識別子です。
最も重要なことは、これらの名称は化学者が常に行っている活動、すなわち化合物の合成と分析に由来しているということである。化合物の合成や分析という化学者が日常的に行う実験活動から生まれた名称です。ゆえに、nativeな識別子と分類されています。
先ほどのハミルトニアン$H$もnativeな識別子です。なぜならば、その命名はイギリスの物理学者ウィリアム・ローワン・ハミルトンに因んでおり、古くから物理のエコシステムで利用されている記号です。

さて、ここでソフトウェアに戻ってみましょう。Purlは、mavenのような特定のパッケージマネージャーのエコシステムで使用されている名前に完全に基づいています。エコシステム内の開発者、ユーザーにとって馴染みのある識別子です。したがって、Purlはintrinsic かつnativeな識別子に分類されます。このような理由から、proposal paperはソフトウェアの命名に関する提案の基本はpurlであるべきだと主張しています。

対して、non-nativeな識別子とは、エコシステムにとって新しく、開発者やユーザーに馴染みのない識別子です。non-nativeな識別子導入する場合、その命名規則の理解や適用に追加の労力が必要となってしまいます。物理や数学でも、新しい概念が登場し、発見者が独自につけた記号などは、non-nativeな識別子に分類できると考えられます。CPEはExtrinsicかつnon-nativeな識別子であり、運用が難しいという課題を持っています。

CPE名はソフトウェア、ハードウェア製品、それぞれに適用できるため、proposal paperはその両方に対応する必要があると主張しています。Purlはパッケージマネージャのエコシステムに対する識別子であり、ハードウェアに適用することはできません。

そこで、ハードウェアの識別子として、既存のGTIN（Global Trade Item Number)とGMN（Global Model Number）という標準的な識別子の利用を提案しています。
これらの識別子は中央レジストリを必要とするため、Extrinsicな識別子です。しかし、ハードウェアエコシステムにとって、nativeな識別子であるという点が、CPEよりも優れているため、候補として提案されています。



## 命名問題の解決策：ソフトウェアの場合
CPEの問題を解決するために期待されている、識別子が**Purl**、**SWID**、**Software Heritage IDs**です。それぞれ、OSS、商用ソフトウェアやプロプライエタリソフトウェア、レガシーソフトウェアを識別します。以下では、これらの識別子を紹介します。
### Purl(package URL)
CPEが抱える命名問題の解決策がIntrinsicかつnativeな識別子を利用することです。そのような識別子で代表的なものがPurl(package URL)です。

- [purl-spec](https://github.com/package-url/purl-spec)

Purl (package URL)はソフトウェアを一意に特定する識別子です。また、そのパッケージがどのエコシステムやリポジトリに存在し、どのようにアクセスできるかを示す場所に関する情報も含んでいます。
Purlは7つの構成要素を含み作成することができ、必須要素は**scheme, type, name**です：
```txt
scheme:type/namespace/name@version?qualifiers#subpath
```
- scheme：これは常に定数pkgとなる (必須)
- type：maven、npm、nuget、gem、pypiなどのパッケージのエコシステムが代入される（必須）
- namespace：パッケージのグループや所有者を表す
例：MavenのグループID、Dockerのリポジトリ名、GitHubのユーザー名など
- name：パッケージエコシステム内のパッケージ名（必須）
- version：パッケージのバージョン番号
- qualifiers：OSやアーキテクチャなど、パッケージに関する追加情報を表す修飾子
- subpath：パッケージ内の特定の場所を示すパス。パッケージのルートからの相対パス

Purlの例です：
```bash
pkg:golang/google.golang.org/genproto#googleapis/api/annotations

pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?packaging=sources
pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?repository_url=repo.spring.io%2Frelease

pkg:npm/%40angular/animation@12.3.1
pkg:npm/foobar@12.3.1

pkg:nuget/EnterpriseLibrary.Common@6.0.1304

pkg:pypi/django@1.11.1

pkg:rpm/fedora/curl@7.50.3-1.fc25?arch=i386&distro=fedora-25
pkg:rpm/opensuse/curl@7.56.1-1.1.?arch=i386&distro=opensuse-tumbleweed
```

Purlベースの脆弱性データベースは既にサポートされており、Sonatype社が[OSS Index database](https://ossindex.sonatype.org/)という脆弱性データベースを公表しています。こちらのデータベースは[Dependency-Track](https://dependencytrack.org/)というソフトウェア構成管理を行うOSS内の脆弱性検知機能にデフォルトで利用されています。ただ、この脆弱性DBが正確でないならば、せっかくPurlを使っていても、結局、CPEと同じ問題にぶつかると思います。

<!--
コンポーネントがどのエコシステムに由来するかに関わらず、コンポーネントのアイデンティティを表す標準化された方法を提供します。複数の脆弱性情報源は、すでに脆弱性検索のためにpurlをサポートしている。これにより、ソフトウェア供給者は、そのビルドに由来するデータでBOMを作成することができ、消費者は、purlをサポートする脆弱性インテリジェンスのあらゆるソースを使用してBOMを分析することができます。BOMの作成、消費、分析は、理論的にはこのアプローチで完全に自動化できる。しかし、NVDはpurlをサポートしていないため、ネーミングの問題により、現在のところNVDの完全自動化は不可能である。

パールは7つのコンポーネントを含むことができるが、必要なのは以下のコンポーネントだけである： 

1. 「Scheme」、これは常に 「pkg 」である。

2. 「パッケージのエコシステムを指す。例としては、「maven」、「npm」、「nuget」、「gem 」がある。

3. 「Name」。これは、パッケージエコシステムにおける製品の名前である。14 例えば、「foo 」がmavenパッケージマネージャで見つかったパッケージである場合、そのパー ルはpkg:maven／fooとなります。もちろん、最初の必須項目が変わることはないので、purlを作成するために必要な新しい情報は2つだけです。オープンソース製品を、例えばmavenビルドフレームワークにあるように参照したい人は、単に 「maven 」とmavenにある製品名を含むpurlを作成する必要があります。その人がミスをしない限り、この2つの情報で作成したパールを使えば、誰でも同じ製品をmavenで見つけることができ、NVDでその製品の脆弱性を特定することができます。14 

ユーザーは、「type 」を製品名が付けられた言語と考えることができる。もしそうであれば、アボカドのパールはpkg:English/avocadoとなる。パラチチのpurlはpkg:Swahili/parachichiとなる。

purlはどの型にも優先しないことに注意してください。ある型（この場合はmavenに含まれる製品）で何かが定義されている場合、それは他の型と同様に重要である。言い換えれば、同じ製品に対して異なる名前を持つ20の型があっても問題にはなりません。Purlには、「generic 」型や 「repository_url 」や 「vcs_url 」といったパラメータなどのメカニズムがあり、他の方法では扱えない多くの特殊なケースを扱うことができます。

20のpurlがそれぞれ一般的に同じ製品であると理解されているものを参照しているとしても、製品の正規名は存在しないことを念頭に置くことが重要です。しかし、これは実は大きな問題を解決する。同じ「製品」であっても、パッケージマネージャが異なれば、インスタンス間にわずかな違いが生じることがよくある。同じ「製品」のインスタンスが異なるパッケージマネージャに存在する場合、そのインスタンスの間に微妙な違いが生じることがよくあります。

Purlはこの質問に対して、最初の選択肢を支持する答えを出します。コードが異なるパッケージマネージャで全く同じであっても、各バージョンは異なる名前を持ち、異なる製品であることに変わりはありません（もちろん、コードはいつでも分岐する可能性があります）。NVD は、ある CVE に関連するすべての purl をリストアップすることができます。

CPEが製品のみを識別するのに対し、purlは製品だけでなくパッケージも識別するため、ライブラリの各モジュールはそれぞれ独自のpurlを持つことになります（各モジュールは少なくとも1つの一意のパッケージに対応するため）。このため、脆弱性がライブラリの 1 つのモジュールにのみ存在する一般的なケースでは、我々の提案が実装されれば、NVD においてその特定のモジュールの purl に対して報告されることになる。

そのモジュールを含む製品のSBOMには、脆弱なモジュールのpurlが記載され、NVDのSBOMで見つかったコンポーネントを検索するツールは、その製品に該当する適切なCVEを特定する。同様に重要なこととして、脆弱なモジュールが製品に存在せず、ライブラリに含まれる他のモジュールが1つ以上存在する場合、NVD検索は製品に該当するCVEを特定しません。

Purlは上記の問題に対する完璧なソリューションではありません。完璧な解決策を私たちは知りません。私たちの解決策が実行不可能なケースはたくさんあり、将来的にはそのようなケースに対処するよう努めます。同時に、私たちは完璧を敵に回したくはありませんし、パールを使うことの利点は欠点をはるかに上回ると信じています。purlを使用することに抵抗があるサプライヤーは、CPEの名前を使って製品の脆弱性を報告することができます。

新しいpurlタイプ 私たちのソリューションのために、purl仕様に2つの新しい 「タイプ 」を追加することを要求しました。これらはそれぞれ、プロプライエタリなコンポーネントとレガシーなオープンソースコンポーネントを少なくとも部分的にカバーするように purl を拡張するものです。
!-->

### SWID
商用ソフトウェアやプロプライエタリソフトウェア（独自のライセンス形態を持つソフトウェア）はパッケージマネージャでは管理されることはほぼないです。プロプライエタリソフトウェアを識別する手段として広く使われているのは、SWIDタグ(Software Identification Tags)です。SWIDタグはサプライヤが作成し、ソフトウェアバイナリとともに提供されます。

<!-- 
Purlは、SWID文書から最も重要なID属性を1つのPurlユニバーサルリソースインジケータ（URI）に抽出し、ソフトウェア作成者、タグ作成者、製品、バージョン、およびtagIDから構成されることにより、SWIDをサポートしています。SWID タグはソフトウェア製品のどのバージョンに対しても作成可能であり、SWID タグの中央データベースは存在しないため、これは、製品の名前および／またはそのサプライヤ名が変更された場合、両方のバージョンが SWID タグを持つ限り、脆弱性は「変更前」と「変更後」の両方のバージョンに対して報告可能であることを意味する。16 したがって、現在の所有者に買収される前の旧バージョンの製品のユーザーは、そのバージョンに適用される脆弱性について知ることができる。
！ -->

## Software Heritage IDs 
現在purlであまり扱われていないもう一つのタイプのソフトウェアは、レガシーソフトウェアです。あるソフトウェア製品がどのパッケージマネージャでも利用できなくなると、保証された一意なpurlを作成することができなくなります。したがって、レガシーソースソフトウェア製品は、他のオープンソース製品とは異なる方法で表現する必要があります。Software Heritageは、ソースコードをアーカイブする非営利団体で、すでにアーカイブされている120億以上のソース・ファイルの中から特定の製品を探し出すために、Software Heritage ID（SWHID）フォーマットを開発しました。SWHIDにはファイルの一意なハッシュが含まれているため、Purl識別子のパッケージ名としてハッシュ値を使用することができます。この場合、typeが「SWH」、nameがハッシュ値で構成されるPurlとなります。

## 命名問題の解決策：ハードウェアの場合
ハードウェアの場合、Purlはありませんので、ハードウェアの識別子としてGTIN （Global Trade Item Number）とGMN（Global Model Number）が提案されています。これらの識別子は中央レジストリが必要になるExtrinsicな識別子です。しかし、GTINとGMNを使用する大きな利点の一つは、世界中の購買システムで既に広く使用されていることであり、これらのシステムに新たな識別子を導入する必要がないnativeな識別子であることです。
### GTIN（Global Trade Item Number）とは
GTIN（グローバルトレードアイテムナンバー）は、国際非営利組織であるGS1が開発した、全ての貿易品を識別するための標準的な識別子です。GTINによる識別は異なるデータベースや組織間で貿易品目を一意に特定することを可能にします。
私たちにとって、身近なGTINの例が、日本の商品のバーコードと紐づいているJANコード (8桁または13桁の番号)や書籍を識別するISBN (13桁の番号)ではないでしょうか。

GTIN識別子がNVDに含まれれば、製品に与えられたバーコードを読み取ることで、ハードウェア機器に影響を及ぼす脆弱性を簡単に特定できるようになると期待されています。

### GMN（Global Model Number）
GMNはGS1が策定した標準で、製品や製品ファミリーを識別するためのものです。



## Summary
|                                           | Extrinsic or Intrinsic? | Native or Non - Native? | 
| ----------------------------------------- | ----------------------- | ----------------------- | 
| CPE  |Extrinsic| Non-Native  | 
| purl  |Intrinsic| Native                         | 
| SWID   | Intrinsic | 不明 | 
| Software Heritage IDs  |Intrinsic | 不明 | 
| Global Trade Identification Number (GTIN) |  Extrinsic| Native                         | 
| Global Model Number (GMN)                 | Extrinsic | Native                         | 


## 最後に
ソフトウェアの識別子のほかにも、自分の知らないところで多くの識別子が提案されていて、驚きました。理学や物流では、こういった識別子によって問題が起きているとあまり聞かないので、うまくできているのだなと思いました。

NVDはありとあらゆる脆弱性の情報が格納されていますが、NVDを過信せず、各ベンダーやエコシステムが出している脆弱性の一時情報を見ていくことも重要なのではないかなと思いました。また、検出した脆弱性が偽陽性だろうがなかろうが、可能な限り取れるだけ集めてきて、トリアージで頑張るしかないのではないか、日ごろから、自社の製品に基本的なセキュリティ対策をしておくことが必要なのではないでしょうか。

## 参考文献
- [A Proposal to Operationalize Component Identification for Vulnerability Management](https://owasp.org/assets/files/posts/A%20Proposal%20to%20Operationalize%20Component%20Identification%20for%20Vulnerability%20Management.pdf)
- [Intrinsic and Extrinsic identifiers – Software Heritage](https://www.softwareheritage.org/2020/07/09/intrinsic-vs-extrinsic-identifiers/)

[^1]: https://en.wikipedia.org/wiki/Chemical_nomenclature