GitはblobでのSHA-1衝突をどのように処理しますか?


543

これはおそらく現実の世界では決して起こらなかったし、決して起こらないかもしれませんが、これを考えてみましょう:gitリポジトリがあり、コミットを行い、非常に不運になったとします:ブロブの1つが同じSHA-1を取得するすでにリポジトリにある別のものとして。問題は、Gitがこれをどのように処理するかということです。単に失敗しますか?2つのblobをリンクして、コンテキストに応じてどちらが必要かを確認する方法を見つけますか?

実際の問題よりも頭の痛いですが、問題は興味深いものでした。


76
かつては頭の体操でしたが、今は潜在的に実際の問題です。
Toby

11
@Tobyこの質問は、プリイメージ攻撃に関するものでした。Googleが示したのは衝突攻撃です -類似していますが少し異なります。違いについて詳しくは、こちらをご覧ください
Saheed 2017

私が提起質問はちょうど約あるとして、この質問の一部は、特にプリイメージ攻撃何であるかが表示されない@Saheed ない、それを悪用については、gitリポジトリで衝突。
Toby

3
@Toby元の頭の体操は、攻撃(プレイメージでも衝突でもない)についてではなく、偶然の衝突についてであり、それは考えられないほど考えられないほどありそうもないことです。サヒードが正しく言っていたのは、これはまだ実際の問題ではないと私は思います。ただし、Googleの衝突攻撃により、Gitの使用方法によってはセキュリティ上の問題が発生する可能性があることは間違いありません。
Andrew W. Phillips

これがわずか320バイトの2回目の衝突です。privacylog.blogspot.com/ 2019/12 / the
William Entriken

回答:


736

この場合の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を作成する場合:プッシュするか、誰かがリポジトリを複製するまで、すべてが正常に見えます。次に、リポジトリが破損していることがわかります。
  3. commitオブジェクトがすでに存在し、同じハッシュでblobを作成する場合:#2と同じ-破損
  4. blobがすでに存在し、同じハッシュでcommitオブジェクトを作成すると、「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は問題ありません。少なくともすべてのケースでエラーで失敗した方がいいと思います。


157
すばらしい答え-ハッシュサイズを減らして実際にどのように動作するかを確認するのは素晴らしいアイデアです。
Gnurou、

4
@Gnurou同意し、その時点でその回答に賛成票を投じた。それらのケースはgitメーリングリストに記載されていましたか?
VonC、2016年

1
ハッシュサイズを削減せずにこれが実際に発生する可能性はどのくらいありますか?
Mathias Bader

4
また、別のハッシュアルゴリズムに移行する計画がある場合はどうしますか。
Pete

9
必ずお読みください-Linus Torvalの説明:plus.google.com/+LinusTorvalds/posts/7tp2gYWQugL
phil_lgr

238

元の回答(2012)(下記のshattered.io2017 SHA1衝突を参照)

Linusからのその古い(2006年の)回答はまだ関連があるかもしれません:

いいえ。同じSHA1を持つ場合、相手からオブジェクトを受け取ったときに、すでに持っているオブジェクトを上書きしないことを意味します。

つまり、衝突が発生すると、特定のリポジトリ内の「以前の」オブジェクトが常に上書きされることになります。ただし、gitオブジェクトネットワークが完全に順序付けされていないDAGを生成するという意味で、「以前」は明らかにリポジトリごとであることに注意してください。したがって、直接の祖先の場合、異なるリポジトリは「以前」が何であるかについて合意します。オブジェクトは、直接関係のない別々のブランチを経由して渡されたため、2つの異なるリポジトリが2つのオブジェクトを異なる順序で取得した可能性があります。

ただし、「以前のバージョンは上書きされます」は、セキュリティの観点からは非常に重要です。gitモデルでは、主に 自分のリポジトリ。
したがって、「git pull」を実行すると、新しい着信オブジェクトは、定義上、既存のオブジェクトよりも信頼性が低くなるため、新しいオブジェクトが古いオブジェクトを置き換えることを許可するのは間違っています。

したがって、衝突の2つのケースがあります。

  • あなたがどういうわけか非常に不運であり、2つのファイルが同じSHA1を持つことになる不注意な種類
    その時点で何が起きるかは、そのファイルをコミットする(または " git-update-index"を実行してインデックスに移動するが、まだコミットされていない)場合、新しいコンテンツのSHA1が計算されますが、古いオブジェクトと一致するため、新しいオブジェクトは作成されず、commit-or-indexは、オブジェクトをます。 (インデックスが古いオブジェクトSHA1と一致するため、「」のようなものがチェックアウトされたコピーを使用することを意味するため)すぐには気づきませんが、ツリーレベルの差分を行う(またはクローンを行う)場合またはプルするか、強制的にチェックアウトします)そのファイルが何かに変わったことに突然気付くでしょう期待したものとはまったく異なる古いオブジェクトを指し示すことになります。 そのため、この種の衝突にかなり早く気付くでしょう。 関連ニュースでは、問題は偶発的な衝突についてどうするかです。 最初に、偶発的な種類の衝突が本当に本当に本当にあることを人々に思い出させてください
    git diff


    ありそうもないので、宇宙の完全な歴史の中でそれを見ることは決してないでしょう。
    しかし、もしそれが起こったとしても、それは世界の終わりではありません。ほとんどの場合、わずかに衝突したファイルを変更し、変更した内容で新しいコミットを強制するだけです(「/* This line added to avoid collision */」というコメントを追加)。次に、危険であることが示されている魔法のSHA1についてgitに教えます。
    したがって、数百万年以上の間、おそらく1つまたは2つの「毒された」SHA1値をgitに追加する必要があります。メンテナンスの問題になることはほとんどありません;)

  • 衝突の攻撃の種類は、誰かが(またはブルート強制)SHA1を破ったため。
    この1は明らかである多くの可能性が高い不注意種類以上が、定義によってそれは常に「リモート」のリポジトリです。攻撃者がローカルリポジトリにアクセスできる場合、攻撃者はあなたをだますためのはるかに簡単な方法を持っているでしょう。
    したがって、この場合、衝突は完全に問題ではありません。攻撃者が意図したものとは異なる「不良」リポジトリを取得しますが、実際には彼の衝突オブジェクトを使用することはないので文字通り、攻撃者は衝突をまったく発見しなかった、しかし、すでに持っているオブジェクトを使用するだけです(つまり、同じSHA1を生成する同一ファイルの「些細な」衝突と100%同等です)。

SHA-256を使用しての質問は、定期的に言及したが、今(2012年)のために作用されていません。
注:2018およびGit 2.19以降、コードはSHA-256を使用するようにリファクタリングされています。


注(ユーモア):特定のSHA1 プレフィックスへのコミットを強制できます。BradFitzpatrickbradfitz)のプロジェクトgitbruteを使用します。

gitbruteは、作成者+コミッターのタイムスタンプのペアをブルートフォースして、結果のgitコミットに希望のプレフィックスが付けられるようにします。

例:https : //github.com/bradfitz/deadbeef


Daniel Dinnyes は、7.1 Git Tools-Revision Selectionのコメントで指摘しいます。

同じ夜に関係のない事件で、プログラミングチームのすべてのメンバーがオオカミに攻撃されて殺される可能性が高くなります。


さらに最近(2017年2月)shattered.ioは、SHA1衝突を偽造する可能性を示しました
(Linus TorvaldsのGoogle+の投稿を含む、別の回答で詳細を参照してください)。

詳細については、Valerie Anita Auroraの暗号ハッシュ関数の寿命」を参照してください。 そのページで、彼女は次のように述べています。

Googleは6500 CPU年と110 GPU年を費やして、セキュリティが重要なアプリケーションでSHA-1の使用をやめる必要があるすべての人を説得しました。
それもかっこいいから

詳細については、以下の個別の回答をご覧ください。


25
ツイスト:追加後も同じハッシュを続けます/* This line added to avoid collision */:Dあなたは2回宝くじに勝つことができます:P
Janus Troelsen


6
その参照に関する@VonC :世界的な狼男の流行の爆発-すべての人類を一掃し、地理的に分散しているにもかかわらず、同じ夜にすべての開発者が恐ろしい死に至った-関連のない事件と見なされましたか?もちろん、それが満月で起こったと仮定すると、明らかに。さて、そのようなシナリオは状況を変えるでしょう。考えても狂気です!それはまったく異なる確率の尺度です!つまり、GITの使用を停止
Daniel Dinnyes 14年

2
gitbruteは特定のSHA1を強制するのではなく、プレフィックスのみを強制することに注意してください(つまり、SHA1全体のサブパート)。SHA1全体(つまり、キーの全長のプレフィックスを使用)を強制すると、「時間がかかりすぎます」。
mb14 2015年

2
その後、追加します@JanusTroelsen:/* This line added to avoid collision of the avoid collision line */
SMG

42

Pro Gitによると:

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

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

少し下に、同じ参照がそのような衝突の可能性を説明しようとします:

以下は、SHA-1衝突が発生するために必要なことを理解するための例です。地球上の65億人すべてがプログラミングを行っており、1秒ごとにLinuxカーネルの履歴全体(100万Gitオブジェクト)に相当するコードを生成し、それを1つの巨大なGitリポジトリにプッシュすると、5年かかります。そのリポジトリには、単一のSHA-1オブジェクトの衝突の50%の確率を持つ十分なオブジェクトが含まれていました。同じ夜に関係のない事件で、プログラミングチームのすべてのメンバーがオオカミに攻撃されて殺される可能性が高くなります。


44
最後の文の数字の出典を教えてください;-)
Joachim Sauer

17
@Jasper:そのリンクは優れたドキュメントですが、チームのすべてのメンバーが同じ夜に関係のない事件でオオカミに攻撃され殺される確率に関する統計含まれていませ
Joachim Sauer、2013年

5
@Jasper:まあ、私がそれを読んだように、テキストは文字通り、同じ夜に65億人のチームメンバーがオオカミに殺される確率が50%より高いと主張しています。しかし、彼の発言に対する私の主な反対は、そのような出来事は世界的な現象でなけれ ならないということです。それはだ思いもよらないこれはのために発生する可能性があることを、無関係の事件。;)
キース・ロバートソン

5
@KeithRobertson私は、世界中の誰もが非常識な量のコードを生成している場合のハッシュ衝突の可能性と比較して、実際のチームメンバー全員が食べられる可能性について、このような状況下でかかる時間に加えて、この投稿が話していると確信しています衝突の50%の確率に到達します(つまり、オオカミの事件は全世界を巻き込んでおらず、50%はオオカミから離れていました)。ただし、そのようなイベントが考えられない場合は、gitハッシュの衝突が発生するはずです。(もちろん、1つは(ほぼ)純粋に偶然に基づいており、もう1つはそうではありませんが、まだです。)
Jasper


23

2012年からの私の以前の回答に加えてshattered.ioとの実際のSHA-1衝突の例がここにあります(2017年2月、5年後)。ここでは、2つの衝突するPDFファイルを作成できます。つまり、SHA- 2番目のPDFファイルの有効な署名として悪用される可能性がある最初のPDFファイルの1つのデジタル署名。
何年にもわたって死の扉が開かれ、広く使用されているSHA1機能は現在は機能しなくなっています」、およびこの図も参照してください。

2月26日更新:Linus はGoogle+の投稿で次の点確認しました

(1)まず、空は落ちていません。セキュリティ署名などに暗号化ハッシュを使用することと、gitなどのコンテンツアドレス可能なシステムの「コンテンツ識別子」を生成するために使用することには大きな違いがあります。

(2)第2に、この特定のSHA1攻撃の性質は、実際に軽減するのが非常に簡単であることを意味し、その軽減のために2セットのパッチが既に投稿されています。

(3)そして最後に、実際には、世界を壊さない他のハッシュへのかなり簡単な移行があります-または古いgitリポジトリですらあります。

その移行については、ハッシュアルゴリズムを表す構造を追加する2018年1四半期のGit 2.16を参照してください。その移行の実装が開始されました。

Git 2.19(2018年第3四半期)以降、GitはSHA-256をNewHashとして選択し、コードへの統合を進めています(SHA1がデフォルト(2019年第2四半期、Git 2.21)ですが、SHA2が後継となります)。


元の回答(2月25日)しかし:

ジョーイ・ヘスこれらのPDFをGitリポジトリで試してみたところ、次のことがわかりました

これには、SHAとサイズが同じ2つのファイルが含まれます。gitがヘッダーをコンテンツの前に付加する方法のおかげで、これらは異なるblobを取得します。

joey@darkstar:~/tmp/supercollider>sha1sum  bad.pdf good.pdf 
d00bbe65d80f6d53d5c15da7c6b4f0a655c5a86a  bad.pdf
d00bbe65d80f6d53d5c15da7c6b4f0a655c5a86a  good.pdf
joey@darkstar:~/tmp/supercollider>git ls-tree HEAD
100644 blob ca44e9913faf08d625346205e228e2265dd12b65    bad.pdf
100644 blob 5f90b67523865ad5b1391cb4a1c010d541c816c1    good.pdf

これらの衝突するファイルに同一のデータを追加すると他の衝突が発生しますが、データの先頭に追加すると発生しません。

だから 攻撃主な方法(コミットの偽造)は次のようになります

  • 通常のコミットオブジェクトを生成します。
  • 選択した接頭辞としてコミットオブジェクト全体とNULを使用します。
  • 同一プレフィックス衝突攻撃を使用して、衝突する良/悪オブジェクトを生成します。
  • ...そして、これは役に立たないオブジェクトとコミットオブジェクトが同じツリーを指しているので役に立たない!

さらに、各ファイルに存在するSHA-1に対する暗号解読衝突攻撃をすでに実行して検出できます。 cr-marcstevens/sha1collisiondetection

Git自体に同様のチェックを追加すると、計算コストが発生しますます。

ハッシュの変更についてLinuxのコメント

ハッシュのサイズとハッシュアルゴリズムの選択は、独立した問題です。
あなたはおそらく256ビットのハッシュに切り替え、それを内部とネイティブのgitデータベースで使用し、デフォルトでは40文字の16進文字列としてのみ ハッシュを表示します(私たちはすでに物事を省略しているように多くの状況)。
そうすれば、特別な " --full-hash"引数(または " --abbrev=64"またはその他-デフォルトは40に短縮されます)で渡されない限り、gitの周りのツールは変更を認識しません。

それでも、(SHA1から別のハッシュ関数への)移行計画は依然として複雑ですが、積極的に研究されています。キャンペーンはある進行中
convert-to-object_id


3月20日更新:GitHubが攻撃の可能性とその保護について詳しく説明します

SHA-1名には、さまざまなメカニズムを通じて信頼を割り当てることができます。たとえば、Gitを使用すると、コミットまたはタグに暗号で署名できます。これにより、コミットまたはタグオブジェクト自体にのみ署名します。これは、SHA-1名を使用して、実際のファイルデータを含む他のオブジェクトをポイントします。これらのオブジェクトの衝突により、有効であるように見えるが、署名者が意図したものとは異なるデータを指す署名が生成される場合があります。このような攻撃では、署名者には衝突の半分しか見えず、被害者には残りの半分が見えます。

保護:

最近の攻撃では、特別な手法を使用して、SHA-1アルゴリズムの弱点を悪用して、より短時間で衝突を発見しています。これらの手法は、衝突ペアのいずれかの半分のSHA-1を計算するときに検出できるパターンをバイトに残します。

GitHub.comは、計算するSHA-1ごとにこの検出を実行し、オブジェクトが衝突ペアの半分であるという証拠がある場合は操作を中止します。これにより、攻撃者がGitHubを使用して、プロジェクトの「無害な」半分の衝突を受け入れるように説得したり、悪意のある半分をホストしたりすることを防ぎます。

マークスティーブンスのsha1collisiondetection」を参照


繰り返しになりますが、2018年第1四半期のGit 2.16にハッシュアルゴリズムを表す構造が追加されたことで、新しいハッシュへの遷移の実装が開始されました。
上記のように、新しくサポートされるハッシュはSHA-256になります。


衝突:1.偶然に発生した衝突ではなく、衝突を作成する試みでした。2. PDFレポートから:計算に費やされた労力は、合計で2 ^ 63.1 SHA-1圧縮に相当し、約6,500 CPU年と100 GPU年かかりました3. MD5とSHA-1から移行する必要がありますが、一般的にはファイルの一意の使用法で問題ありません。
zaph 2017

WebKitがテストのために衝突するPDFをチェックインしたことは注目に値します。git-svnミラーインフラストラクチャを破壊しました
168774#

1
@dahlbyk確かに注目に値します...回答でそれを指摘した点で(git-svn間接的ではありますが、間接的にではありますが、「これには何らかの問題があります」のリンクがそれを参照しています)
VonC

1
@Mr_and_Mrs_Dいいえ、まだエラーで失敗していません。衝突の検出を容易にする大きなパッチが進行中です:marc.info/?l
git&

1
@Mr_and_Mrs_D SEe edit 4 in stackoverflow.com/posts/42450327/revisions:少なくともGitHubに更新したときに失敗します。
VonC 2017年

6

暗号学者は祝うと思います。

SHA-1に関するWikipediaの記事からの引用:

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


7
要点は、SHA1に欠陥が見つかり、Gitが導入された頃のことです。また、確率は非線形です。宝くじを50年間プレイしたからと言って、勝つ可能性が高くなるわけではありません。毎回同じチャンスがあります。初めて遊ぶ人でも勝つことができます。
0xC0000022L 2014

これは衝突を見つける攻撃にすぎません。yつまり、h(x) == h(y) `はSSL証明書などの任意のデータに対する深刻な脅威であることがわかりますが、これはGitには影響しません。メッセージを持つxメッセージにそれを修正することができるx'というh(x) == h(x')。したがって、この攻撃はGitを弱めません。また、Gitはセキュリティ上の理由からSHA-1を選択していません。
Hauleth 2017

衝突が発見されました-まだ直接gitを悩ませている衝突ではありません。 stackoverflow.com/questions/42433126/...
ウィレムHengeveld

2 ^ 69は約600エクサオペレーションです。8年後、A100でアップグレードされたNvidiaのSaturnVスーパーコンピューターは4.6 ExaOPSを実行できるため、2分以上でこれを解決したり、数日でブルートフォース攻撃を実行したりできます。
qdin

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依存関係がなくても前述のものを実行した場合、それは多額のお金になります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.