ソフトウェア・サプライチェーン・インテグリティ保証ツール in-toto の紹介
はじめに
ソフトウェア・サプライチェーン・インテグリティ保証とは
ソフトウェア・サプライチェーン・インテグリティ保証とは、ソフトウェアのコーディング、ビルド、パッケージング、利用の一連のソフトウェア・ライフサイクルの中で、第三者によるコードの改ざんやパッケージのすり替えなどの攻撃がないことの保証のことです。
例えば、Google は、ソフトウェア・サプライチェーン・インテグリティ保証を次のような図でモデル化して説明しています。
出典: https://security.googleblog.com/2021/06/introducing-slsa-end-to-end-framework.html
ソフトウェアが開発者(Developer)から利用者(Use)に届くまでのプロセスの中で、A ~ Hまでの攻撃が考えられます。
- A: 不正なコードの送信
- B: ソース管理プラットフォームの汚染
- C: コードの改ざん
- D: ビルドプラットフォームの汚染
- E: 不正な依存関係
- F: CI/CDをバイパス
- G: パッケージリポジトリの汚染
- H: 不正なパッケージの使用
これらの攻撃に対し、パッケージマネージャやリポジトリといった従来のツールは脆弱で、これらの攻撃を防御することができませんでした。
ソフトウェア・サプライチェーン・インテグリティ・フレームワークとは
従来のツールの脆弱性を補うために登場したツールが、ソフトウェア・サプライチェーン・インテグリティ・フレームワークです。
このツールは、ソフトウェア・ライフサイクルの中で使われるSCMツール、パッケージマネージャなどの従来のツールと連携しつつ、ソフトウェア・サプライチェーン・インテグリティ保証のための機能を提供します。
ソフトウェア・サプライチェーン・インテグリティ・フレームワークとしては、次の2つのツールが知られています。
- SALSA
- in-toto
SLSA
SLSA (Supply-chain Levels for Software Artifacts) は、 Google が使用し、提案するソフトウェア・サプライチェーン・インテグリティ・フレームワークの仕様です。
https://japan.zdnet.com/article/35172644/
https://security.googleblog.com/2021/06/introducing-slsa-end-to-end-framework.html
SLSAは、次のようなインテグリティ保証のレベルやその要件を定義します。
レベル | 説明 | 例 |
---|---|---|
1 | ビルドプロセスのドキュメント | 未署名の起源(provenance) |
2 | ビルドサービスの耐タンパー性 | ホストされたソース/ビルド、署名済み起源 |
3 | 特定の脅威に対する余分な抵抗を防ぎます | ホストのセキュリティ管理、改ざん不可能な起源 |
4 | 最高レベルの信頼と信頼 | 二者間レビュー + 堅牢ビルド(hermetic build) |
一方で、SLSA 自身にはインテグリティ保証のためのツールや参照実装を含んでいません。
その代わり、SLSA のコミュニティは、後述する in-toto を使ったデモ を例示しています。
in-toto とは
in-toto とは、インテグリティ保証を実現するためのツールです。
ちなみに、in-toto はラテン語で「全体」を意味します。
in-toto は、ソフトウェア製品の製造開始からエンドユーザーへのインストールまでの整合性を確保するように設計されています。整合性は、ソフトウェア・ライフサイクル上のどのステップが、誰によって、どの順序で、実行されたかの記録を署名付きで保存することで実現します。
ちなみに、in-toto は米国国立科学財団(NSF)、国防高等研究計画局(DARPA)、空軍研究所(AFRL)および Linux Foundation によって支援されています。
デモ
デモ概要
in-totoで紹介されているデモのプログラムを紹介します。
https://github.com/in-toto/demo
このソフトウェア・サプライチェーン・レイアウトには、次の登場人物がいます。
- Alice: プロジェクトの管理者、所有者。
- Bob: プロジェクトの開発者。
- Carl: ソフトウェアをパッケージ化(tar ball)する者。
- Client: 最終製品(final product)の利用者
準備
まず、demoプロジェクトをクローンして、poetryプロジェクトを作成します。
git clone https://github.com/in-toto/demo
cd ../demo
poetry init
つぎに、in-totoをインストールします。
poetry add in-toto
ソフトウェア・サプライチェーン・レイアウトの定義(Alice)
まず、ソフトウェア・サプライチェーン・レイアウトを定義します。
cd owner_alice
poetry run python create_layout.py
create_layout.py は、次のような署名付き レイアウト・メタデータ・ファイル を生成します。
{
"signatures": [
{
"keyid": "...",
"sig": "..."
}
],
"signed": {
"inspect": [
{
"_type": "inspection",
"name": "untar",
"run": [
"tar",
"xzf",
"demo-project.tar.gz"
]
}
],
"keys": {
// ...
},
"readme": "",
"steps": [
{
"name": "clone",
"expected_command": [
"git",
"clone",
"https://github.com/in-toto/demo-project.git"
]
},
{
"name": "update-version",
"expected_command": []
},
{
"name": "package",
"expected_command": [
"tar",
"--exclude",
".git",
"-zcvf",
"demo-project.tar.gz",
"demo-project"
]
}
]
}
}
このファイルのポイントは、次の点です。
-
signatures
にはAliceの秘密鍵による署名が付されます。これにより、悪意ある改ざんが困難になります。 -
inspect
には、パッケージを検証するためのコマンドや検証条件が定義されます。このデモではパッケージtar ball
をuntar
コマンドで解凍し、その中のファイルの改ざん有無を検証します。 -
steps
には、正規のビルドステップが定義されます。後述のリンク・メタデータ・ファイルがステップと紐付き、正規のビルドステップが正規のユーザにより実行されたことが保証されます。
プロジェクトのソースコードのクローンを作成する(Bob)
Bob は、git clone
の代わりに、in-toto-run clone
を使用して、GitHubからプロジェクトリポジトリのクローンを作成し、メタデータを記録します。
cd ../functionary_bob
poetry run in-toto-run --step-name clone --products demo-project/foo.py --key bob -- git clone https://github.com/in-toto/demo-project.git
このとき作成される署名付きメタデータは次の通りです。
{
"signatures": [
{
"keyid": "...",
"sig": "..."
}
],
"signed": {
"_type": "link",
"byproducts": {
"return-value": 128,
"stderr": "",
"stdout": ""
},
"command": [
"git",
"clone",
"https://github.com/in-toto/demo-project.git"
],
"environment": {},
"materials": {},
"name": "clone",
"products": {
"demo-project/foo.py": {
"sha256": "..."
}
}
}
}
バージョン番号の更新(Bob)
開発者である Bob は、リリース担当の Carl がソースコードをパッケージ化する前に、in-toto-record
を使用して、リンク・メタデータ・ファイルを生成します。
リンク・メタデータ・ファイルは、ファイルの状態を記録します。
poetry run in-toto-record start --step-name update-version --key bob --materials demo-project/foo.py
このとき作成される署名付きメタデータは次の通りです。
{
"signatures": [
{
"keyid": "...",
"sig": "..."
}
],
"signed": {
"_type": "link",
"byproducts": {},
"command": [],
"environment": {},
"materials": {
"demo-project/foo.py": {
"sha256": "..."
}
},
"name": "update-version",
"products": {}
}
}
このファイルのポイントは、次の点です。
-
signatures
にはAliceの秘密鍵による署名が付されます。これにより、悪意ある改ざんが困難になります。 -
materials
には、ファイルごとのハッシュ値が記録されます。
次に、Bob はdemo-project/foo.py
を更新します。
cat <<EOF > demo-project/foo.py
VERSION = "foo-v1"
EOF
最後に、 Bob は変更後のファイルの状態を記録し、 リンク・メタデータ・ファイル を生成します。
poetry run in-toto-record stop --step-name update-version --key bob --products demo-project/foo.py
このとき作成される署名付きメタデータは次の通りです。
{
"signatures": [
{
"keyid": "...",
"sig": "..."
}
],
"signed": {
"_type": "link",
"byproducts": {},
"command": [],
"environment": {},
"materials": {
"demo-project/foo.py": {
"sha256": "..."
}
},
"name": "update-version",
"products": {
"demo-project/foo.py": {
"sha256": "..."
}
}
}
}
このファイルのポイントは、次の点です。
-
signatures
にはAliceの秘密鍵による署名が付されます。これにより、悪意ある改ざんが困難になります。 -
materials
には、ファイルごとのハッシュ値が記録されます。
これで、 Bob の作業は完了です。
リリース担当の Carl にソースコードを送ります。
cp -r demo-project ../functionary_carl/
パッケージ(Carl)
Carl は、 in-toto-run package
コマンドを実行して、ソフトウェア・プロジェクトのパッケージを作成します。
cd ../functionary_carl
poetry run in-toto-run --step-name package --materials demo-project/foo.py --products demo-project.tar.gz --key carl -- tar --exclude ".git" -zcvf demo-project.tar.gz demo-project
このとき作成される署名付きメタデータは次の通りです。
{
"signatures": [
{
"keyid": "...",
"sig": "..."
}
],
"signed": {
"_type": "link",
"byproducts": {
"return-value": 0,
"stderr": "",
"stdout": ""
},
"command": [
"tar",
"--exclude",
".git",
"-zcvf",
"demo-project.tar.gz",
"demo-project"
],
"environment": {},
"materials": {
"demo-project/foo.py": {
"sha256": "..."
}
},
"name": "package",
"products": {
"demo-project.tar.gz": {
"sha256": "..."
}
}
}
}
このファイルのポイントは、次の点です。
-
signatures
には、Aliceの秘密鍵による署名が付されます。これにより、悪意ある改ざんが困難になります。 -
command
には、パッケージを生成する際に使用したコマンドが記録されます。 -
materials
には、ファイルごとのハッシュ値が記録されます。 -
products
には、パッケージごとのハッシュ値が記録されます。
最終製品の確認(Client)
Client に、最終製品をリリースします。
final_product/
ディレクトリ に、リリースするパッケージとメタデータを格納します。
cd ..
cp owner_alice/root.layout functionary_bob/clone.776a00e2.link functionary_bob/update-version.776a00e2.link functionary_carl/package.2f89b927.link functionary_carl/demo-project.tar.gz final_product/
この結果、final_product/
ディレクトリは次のような構成になります。
final_product/
├── clone.776a00e2.link
├── demo-project.tar.gz
├── package.2f89b927.link
├── root.layout
└── update-version.776a00e2.link
Client は、信頼できるソースからAliceの公開鍵を取得して、 in-toto-verify
コマンドを実行して、最終製品を検証します。
cd final_product
cp ../owner_alice/alice.pub .
poetry run in-toto-verify --layout root.layout --layout-key alice.pub
echo $?
0
が表示されれば、検証がうまく機能したことを意味します。
このとき作成されるインスペクションの結果を示すリンク・メタデータ・ファイルは次の通りです。
{
"signatures": [],
"signed": {
"_type": "link",
"byproducts": {
"return-value": 0,
"stderr": "",
"stdout": ""
},
"command": [
"tar",
"xzf",
"demo-project.tar.gz"
],
"environment": {},
"materials": {
".keep": {
"sha256": "..."
},
"alice.pub": {
"sha256": "..."
},
"demo-project.tar.gz": {
"sha256": "..."
},
"root.layout": {
"sha256": "..."
}
},
"name": "untar",
"products": {
".keep": {
"sha256": "..."
},
"alice.pub": {
"sha256": "..."
},
"demo-project.tar.gz": {
"sha256": "..."
},
"demo-project/foo.py": {
"sha256": "..."
},
"root.layout": {
"sha256": "..."
}
}
}
}
in-toto-verify
コマンドは、次のことを検証します。
- レイアウト・メタデータ・ファイルの署名が有効期限内であること
- レイアウトの定義に従っており、Alice の秘密鍵で署名されたこと
- ビルドの各ステップが、許可された者によって実行され、署名されたこと
- 記録されたマテリアルとプロダクトはアーティファクトのルールに準拠していること
- インスペクションの結果が期待通りであること
ソフトウェアサプライチェーンの改ざん
悪意ある人が、ソフトウェア・サプライチェーンを改ざんすることを模擬します。
foo.py
に悪意あるコード something evil
を追加します。
cd ../functionary_carl
echo something evil >> demo-project/foo.py
Carl は、改ざんに気づかずにパッケージングします。
poetry run in-toto-run --step-name package --materials demo-project/foo.py --products demo-project.tar.gz --key carl -- tar --exclude ".git" -zcvf demo-project.tar.gz demo-project
Client は、改ざんに気づかずに生成された最終製品を受領します。
cd ..
cp owner_alice/root.layout functionary_bob/clone.776a00e2.link functionary_bob/update-version.776a00e2.link functionary_carl/package.2f89b927.link functionary_carl/demo-project.tar.gz final_product/
悪意のある製品の検証
Client は、悪意ある製品を検証します。
$ cd final_product
$ poetry run in-toto-verify --layout root.layout --layout-key alice.pub
(in-toto-verify) RuleVerificationError: 'DISALLOW *' matched the following artifacts: ['demo-project/foo.py']
Full trace for 'expected_materials' of item 'package':
Available materials (used for queue):
['demo-project/foo.py']
Available products:
['demo-project.tar.gz']
Queue after 'MATCH demo-project/* WITH PRODUCTS FROM update-version':
['demo-project/foo.py']
$ echo $?
1
戻り値が 1
ならば、改ざんを検出できたことを意味します。
まとめ
ソフトウェア・サプライチェーン・インテグリティ保証の考え方とそのツール in-toto
を紹介しました。
Discussion