10月1日、GitButlerが「Git 3.0's upcoming SHA-256 default will be a costly mistake」と題した記事を公開した。この記事では、Git 3.0でSHA-256がデフォルトになることへの技術的な異議と、その移行コストの大きさについて詳しく紹介されている。以下に、その内容を紹介する。
「壊れている」の意味を履き違えている
GitはコンテンツアドレッサブルなKey-Valueデータベースだ。ファイルやコミットはSHA-1ハッシュ値をキーとして管理されており、Linus Torvaldsが2005年にGitを設計して以来、この仕組みは20年間機能し続けてきた。
問題の発端は、2017年のSHAtteredと2020年のSHA-1 is a Shamblesという2本の論文だ。いずれもSHA-1に意図的なハッシュ衝突を生じさせられることを示したもので、これを受けてGitコミュニティはSHA-256への移行を進めてきた。Git 3.0では新規リポジトリのデフォルトがSHA-256になる予定である。
しかし記事の著者Scott Chaconが強調するのは、「壊れている(broken)」という言葉の意味が一般的な理解とまったく異なるという点だ。
暗号学的な文脈での「壊れている」とは、「意図的に衝突を作り出せる可能性がゼロではない」という意味にすぎない。偶然に2つの異なるファイルが同じハッシュ値になる可能性は、1つのプロジェクトに約140京億個(1.4 septillion)のファイルが存在しなければ確率的に起きない。現実には起きない数字だ。
本当に怖いのは「第二原像攻撃」か「衝突攻撃」か
ハッシュ関数への攻撃には2種類ある。
- 衝突攻撃(Collision Attack): 攻撃者が最初から2つのファイルを用意する。一方は無害なファイル、もう一方は悪意あるファイルで、両者のハッシュ値を意図的に一致させておく。無害なファイルで信頼を得た後、悪意あるファイルに差し替える。
- 第二原像攻撃(Second-preimage Attack): 既存の特定ファイルと同じハッシュ値を持つ別のファイルを生成し、差し替える。
記事が指摘する重要な点は、第二原像攻撃はSHA-1どころかMD5でも現実的には不可能だという事実だ。
地球上のGPU約30億台をすべてRTX 5090に置き換え、全台がMD5の計算だけに専念したとしても、特定のハッシュ値に対応するデータを総当たりで見つけるのに期待値で約160億年かかる計算になる。
$$\frac{3.4 \times 10^{38} \text{ checks}}{3 \times 10^9 \text{ GPUs} \times 2.2 \times 10^{11} \text{ hashes/sec}} \approx 5.2 \times 10^{17} \text{ seconds} \approx 160 \text{ 億年}$$
つまり現実的な攻撃ベクターは衝突攻撃のみ。しかし衝突攻撃が成立するには、攻撃者が悪意あるファイルをあらかじめ正規リポジトリに送り込む必要がある。そしてそのファイルを被害者が「知らないソースから」取得しなければならない。
Linus Torvalds自身、Gitの誕生直後の2005年のメールでこう述べている。
「SHA-1を"セキュリティ"だと考えるべきではない。本当のセキュリティは分散(distribution)にある。」
— Linus Torvalds, 2005
信頼の根拠は「どこからpullするか」であって、ハッシュアルゴリズムの強度ではない。
実際の攻撃はもっとずっと安上がりだ
記事が最も鋭く指摘するのは、現実のサプライチェーン攻撃はGPUを使ったハッシュ衝突より何桁も安く、簡単に実行できるという点だ。
実際に起きているのはこういうことだ。攻撃者はGPUファームにお金をかけるのではなく、人気のnpmパッケージのメンテナを社会工学的に操り、書き込み権限を奪う。そうすれば、package.jsonにそのパッケージを持つ何百万ものプロジェクトが、検証なしに悪意あるコードを取り込む。
あるいはもっと単純に、メンテナ疲れで放置されたOSSプロジェクトを4万ドルで買い取るだけでいい。GPUレンタルは不要、1日で完了、任意のファイルを任意の内容に差し替えられる。
「ハッシュ衝突攻撃は、対価を払っていないOSSメンテナや信頼性の低いパッケージ管理プラットフォームが存在する世界において、システムに不正なコードを送り込む方法として、おそらく最も愚かな方法だ。」
Git 3.0移行の現実的なコスト
ではなぜ問題なのか。SHA-256への移行そのものよりも、デフォルト変更がもたらす混乱のコストが問題だ。
git init --object-format=sha256 で作ったリポジトリを試してみると、コミットハッシュが64文字になる。
commit 11043f6a3be7d21e999dc84550886306bee65f4faf4fc9226979108fd1a0b1af (HEAD -> main)
現時点では、このリポジトリをGitHubにpushできない(Git 3.0リリース時には対応される見込みだが)。さらに深刻なのは、SHA-1リポジトリとSHA-256リポジトリは混在できない点だ。
サーバー側でリポジトリを作る際に、どちらのフォーマットかを事前に指定しなければならない。git initをどのバージョンのGitで実行したかを把握していないと詰まる。Googleはこの問題に対応するための社内準備について講演を行っているが、容易ではない。
しかも移行が完了したとしても、将来量子コンピュータがSHA-256を破れば、また同じ議論が繰り返されることになる。「暗号学的整合性」と「信頼」を混同し続ける限り、この問題は終わらない。
詳細はGit 3.0's upcoming SHA-256 default will be a costly mistakeを参照していただきたい。