web-dev-qa-db-ja.com

Gitはブロブ上でのSHA-1衝突をどのように処理するのでしょうか?

これはおそらく現実の世界では決して起こらなかったし、起こらないかもしれませんが、考えてみましょう:あなたがgitリポジトリを持っていて、コミットして、非常に不運になったとしましょう。すでにリポジトリに入っているものとして。質問は、Gitはこれをどのように処理するのでしょうか。単に失敗する? 2つのBLOBをリンクする方法を見つけて、コンテキストに従ってどちらが必要かを確認しますか?

実際の問題よりも頭がおかしいですが、私は問題が面白いと思いました。

513
Gnurou

この場合のGitの動作を正確に調べるために実験を行いました。これはバージョン2.7.9〜rc0 + next.20151210(Debianバージョン)のものです。私は基本的に次のdiffを適用してgitを再構築することでハッシュサイズを160ビットから4ビットに減らしました。

--- git-2.7.0~rc0+next.20151210.orig/block-sha1/sha1.c
+++ git-2.7.0~rc0+next.20151210/block-sha1/sha1.c
@@ -246,6 +246,8 @@ void blk_SHA1_Final(unsigned char hashou
    blk_SHA1_Update(ctx, padlen, 8);

    /* Output hash */
-   for (i = 0; i < 5; i++)
-       put_be32(hashout + i * 4, ctx->H[i]);
+   for (i = 0; i < 1; i++)
+       put_be32(hashout + i * 4, (ctx->H[i] & 0xf000000));
+   for (i = 1; i < 5; i++)
+       put_be32(hashout + i * 4, 0);
 }

それから私はいくつかのコミットをして、次のことに気づきました。

  1. 同じハッシュを持つBLOBがすでに存在する場合は、警告はまったく表示されません。すべて問題ないようですが、あなたがプッシュしたり、誰かがクローンを作成したり、元に戻ったりすると、最新のバージョンが失われてしまいます(上記の説明に従って)。
  2. ツリーオブジェクトがすでに存在していて、同じハッシュでBLOBを作成した場合:Pushを試みるか、誰かがあなたのリポジトリを複製しようとするまでは、すべて正常に見えるでしょう。そうすると、リポジトリが破損していることがわかります。
  3. コミットオブジェクトがすでに存在していて同じハッシュでBLOBを作成した場合:#2と同じ - 破損
  4. BLOBがすでに存在していて、同じハッシュでコミットオブジェクトを作成した場合、 "ref"を更新すると失敗します。
  5. BLOBがすでに存在していて、同じハッシュでツリーオブジェクトを作成した場合コミットを作成するときに失敗します。
  6. ツリーオブジェクトがすでに存在していて、同じハッシュでコミットオブジェクトを作成すると、 "ref"を更新するときに失敗します。
  7. ツリーオブジェクトがすでに存在していて、同じハッシュを持つツリーオブジェクトを作るのであれば、すべて問題ないようです。しかし、コミットすると、すべてのリポジトリが間違ったツリーを参照します。
  8. コミットオブジェクトがすでに存在していて、同じハッシュでコミットオブジェクトを作成した場合、すべて問題ないように見えます。しかし、コミットすると、コミットは作成されず、HEADポインタは古いコミットに移動されます。
  9. コミットオブジェクトがすでに存在していて、同じハッシュでツリーオブジェクトを作成した場合、コミットを作成するときに失敗します。

#2では、 "git Push"を実行すると、通常このようなエラーになります。

error: object 0400000000000000000000000000000000000000 is a tree, not a blob
fatal: bad blob object
error: failed to Push some refs to Origin

または

error: unable to read sha1 file of file.txt (0400000000000000000000000000000000000000)

ファイルを削除してから "git checkout file.txt"を実行した場合。

#4と#6では、通常次のようなエラーが発生します。

error: Trying to write non-commit object
f000000000000000000000000000000000000000 to branch refs/heads/master
fatal: cannot update HEAD ref

"git commit"を実行しているとき。この場合、(変更されたタイムスタンプのために)これは新しいハッシュを作成するので、通常はもう一度 "git commit"と入力します。

#5と#9では、通常次のようなエラーが発生します。

fatal: 1000000000000000000000000000000000000000 is not a valid 'tree' object

"git commit"を実行したとき

誰かがあなたの壊れたリポジトリを複製しようとすると、彼らは通常次のようなものを見るでしょう:

git clone (one repo with collided blob,
d000000000000000000000000000000000000000 is commit,
f000000000000000000000000000000000000000 is tree)

Cloning into 'clonedversion'...
done.
error: unable to read sha1 file of s (d000000000000000000000000000000000000000)
error: unable to read sha1 file of tullebukk
(f000000000000000000000000000000000000000)
fatal: unable to checkout working tree
warning: Clone succeeded, but checkout failed.
You can inspect what was checked out with 'git status'
and retry the checkout with 'git checkout -f HEAD'

私が「心配」しているのは、2つのケース(2、3)では警告なしにリポジトリが破損し、3つのケース(1、7、8)ではすべて問題ないように思われるが、リポジトリの内容は予想と異なるすることが。クローンを作成したり引っ張ったりする人々は、あなたが持っているものとは異なる内容を持つでしょう。 4、5、6、9の場合はエラーで停止するので問題ありません。少なくともすべてのケースでエラーで失敗した方が良いと思います。

680
Ruben

によると プロGit

たまたまリポジトリにある以前のオブジェクトと同じSHA-1値にハッシュされているオブジェクトをコミットした場合、Gitは以前のオブジェクトをGitデータベースに既に見て、それがすでに書き込まれていると見なします。ある時点でそのオブジェクトをもう一度チェックアウトしようとすると、必ず最初のオブジェクトのデータが取得されます。

それで失敗することはありませんが、新しいオブジェクトも保存されません。
コマンドラインでそれがどのように見えるかわかりませんが、それは確かに混乱を招くでしょう。

もう少し言えば、同じ参照がそのような衝突の可能性を説明しようとしている:

これが、SHA-1の衝突を起こすのに必要なことを理解するための例です。地球上の65億人の人間全員がプログラミングをしていて、1秒ごとにLinuxカーネルの全歴史(100万Gitオブジェクト)に相当するコードを作成し、それを1つの巨大なGitリポジトリに押し込めばそのリポジトリには、単一のSHA-1オブジェクトが衝突する確率が50%になるのに十分な数のオブジェクトが含まれていました。あなたのプログラミングチームのすべてのメンバーが、同じ夜に無関係の事件でオオカミによって攻撃され殺される可能性がより高いです。

41
Mat

私は暗号学者たちが祝うだろうと思います。

SHA-1に関する ウィキペディアの記事からの引用

2005年2月、Xiaoyun Wang、Yiqun Lisa Yin、Hongbo Yuによる攻撃が発表されました。攻撃は、フルバージョンのSHA-1で衝突を見つけることができ、2 ^ 69未満の操作しか必要としません。 (ブルートフォース検索では2 ^ 80の操作が必要になります。)

6

SHA-1のようなハッシュのためのいくつかの異なる攻撃モデルがありますが、通常議論されるものはMarc Stevensの HashClash ツールを含む衝突検索です。

"2012年現在、SHA-1に対する最も効率的な攻撃はMarc Stevens [34]によるものと見なされ、クラウドサーバーからCPUパワーを借りて単一のハッシュ値を破るための推定費用は2.77Mでした。 "

皆さんが指摘したように、あなたはgitとハッシュ衝突を強いることができますが、そうしても別のリポジトリの既存のオブジェクトを上書きすることはありません。 git Push -f --no-thinでも既存のオブジェクトを上書きしないと思いますが、100%確実というわけではありません。

そうは言っても、もしあなたがリモートリポジトリにハックしたら、あなたの偽のオブジェクトをもっと古いものにすることができます。 githubなどあなたが注意深かったならば、多分あなたは新しいユーザーがダウンロードしたハッキン​​グされたバージョンを紹介することができます。

しかし私は、プロジェクトの開発者がすることができる多くのことがあなたの数百万ドルものハックを露呈させるか偶然に破壊する可能性があると思います。特に、あなたがハックしていない開発者が、影響を受けたファイルを修正した後に前述のgit Push --no-thinを実行したことがあるとしたら、時には--no-thinに依存しなくても、かなりの費用がかかります。

5
Jeff Burdges