Как Git обрабатывать столкновение SHA-1 на блобе?

Это, вероятно, никогда не происходило в реальном мире и, возможно, никогда не произойдет, но рассмотрим это: скажем, у вас есть репозиторий git, совершите фиксацию и получите очень очень неудачное: один из блобов заканчивается тем, что тот же SHA-1, что и в вашем репозитории. Вопрос в том, как бы справиться с этим git? Просто провалиться? Найдите способ связывания двух блоков и проверьте, какой из них необходим в соответствии с контекстом?

Больше мозгового дразнилки, чем реальная проблема, но я нашел проблему интересной.

Ответы

Ответ 1

Я сделал эксперимент, чтобы узнать, как именно будет выглядеть Git в этом случае. Это версия 2.7.9 ~ rc0 + next.20151210 (версия Debian). Я просто уменьшил размер хэша с 160-бит до 4-бит, применив следующий diff и rebuilding git:

--- 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);
 }

Затем я сделал несколько коммитов и заметил следующее.

  • Если blob уже существует с тем же хешем, вы не получите никаких предупреждений вообще. Кажется, все в порядке, но когда вы нажимаете, кто-то клонирует, или вы возвращаетесь, вы потеряете последнюю версию (в соответствии с тем, что объяснено выше).
  • Если объект дерева уже существует, и вы делаете blob с тем же хэшем: все будет казаться нормальным, пока вы не попытаетесь нажать, или кто-то клонирует ваш репозиторий. Затем вы увидите, что репо повреждено.
  • Если объект фиксации уже существует и вы делаете blob с тем же хешем: то же, что и # 2 - поврежденный
  • Если blob уже существует, и вы создаете объект фиксации с тем же хешем, он будет терпеть неудачу при обновлении "ref".
  • Если blob уже существует, и вы создаете объект дерева с тем же хешем. Это не сработает при создании фиксации.
  • Если объект дерева уже существует и вы создаете объект фиксации с тем же хэшем, он будет терпеть неудачу при обновлении "ref".
  • Если объект дерева уже существует и вы создаете объект дерева с тем же хешем, все будет выглядеть нормально. Но когда вы зафиксируете, весь репозиторий будет ссылаться на неправильное дерево.
  • Если объект фиксации уже существует и вы создаете объект фиксации с тем же хэшем, все будет выглядеть нормально. Но когда вы совершаете фиксацию, фиксация никогда не будет создана, а указатель HEAD будет перенесен на старую фиксацию.
  • Если объект фиксации уже существует и вы создаете древовидный объект с тем же хэшем, он будет терпеть неудачу при создании фиксации.

Для # 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,3) репозиторий становится коррумпированным без каких-либо предупреждений, а в 3 случаях (1,7,8) все выглядит нормально, но содержимое репозитория отличается от того, что вы ожидаете этого. Люди, клонирующие или вытягивающие, будут иметь другое содержание, чем то, что у вас есть. Случаи 4,5,6 и 9 в порядке, так как он остановится с ошибкой. Полагаю, было бы лучше, если бы он не ошибся, по крайней мере, во всех случаях.

Ответ 2

Оригинальный ответ (2012) (см. shattered.io 2017 коллизия SHA1 ниже)

Этот старый (2006 г.) ответ Линуса все еще может быть актуален:

Нету. Если он имеет тот же SHA1, это означает, что когда мы получим объект с другого конца, мы не будем перезаписывать объект, который у нас уже есть.

Так что получается, что если мы когда-либо увидим коллизия, "более ранний" объект в любом конкретном хранилище всегда будет переопределяться. Но обратите внимание, что "ранее" очевидно для каждого хранилища, в том смысле, что сеть объектов git генерирует группу обеспечения доступности баз данных, которая не полностью упорядочена, поэтому в то время как разные хранилища согласятся с тем, что "раньше" в случае прямого происхождения, если объект прошел через отдельные и не связанные напрямую ветки, два разных хранилища, очевидно, могли получить два объекта в разном порядке.

Тем не менее, "более ранняя переопределение" очень важна с точки зрения безопасности: помните, что модель git заключается в том, что вы должны в первую очередь доверять только своему собственному репозиторию.
Поэтому, если вы выполняете " git pull ", новые входящие объекты по определению менее надежны, чем объекты, которые у вас уже есть, и поэтому было бы неправильно разрешать новому объекту заменять старый.

Итак, у вас есть два случая столкновения:

  • непреднамеренный вид, когда вам как-то очень-очень не повезло, и два файла в конечном итоге имеют один и тот же SHA1.
    В этот момент происходит то, что когда вы фиксируете этот файл (или делаете " git-update-index ", чтобы переместить его в индекс, но еще не зафиксировали), SHA1 нового содержимого будет вычислен, но так как он соответствует старому объекту, новый объект не будет создан, и фиксация или индекс заканчивает указанием на старый объект.
    Вы не заметите сразу (так как индекс будет соответствовать старому объекту SHA1, а это означает, что что-то вроде " git diff " будет использовать извлеченную копию), но если вы когда-нибудь сделаете diff на уровне дерева (или вы клон или вытащить, или вызвать извлечение) вы вдруг заметите, что этот файл изменился на что-то совершенно другое, чем вы ожидали.
    Таким образом, вы, как правило, заметили такое коллизия довольно быстро.
    В связанных новостях вопрос состоит в том, что делать с непреднамеренным коллизиям.
    Прежде всего, позвольте мне напомнить людям, что непреднамеренное коллизия действительно очень маловероятно, поэтому мы, скорее всего, никогда не увидим его за всю историю вселенной.
    Но если это произойдет, это не конец света: вам, скорее всего, придется просто изменить файл, который слегка столкнулся, и просто вызвать новый коммит с измененным содержимым (добавьте комментарий, говорящий " /* This line added to avoid collision */ "), а затем обучите git магии SHA1, которая, как было показано, опасна.
    Таким образом, в течение пары миллионов лет, возможно, нам придется добавить одно или два "отравленных" значения SHA1 в git. Это очень маловероятно, чтобы быть проблемой обслуживания;)

  • Нападение нападающего, потому что кто-то сломал (или перебор) SHA1.
    Этот файл явно более вероятен, чем непреднамеренный, но по определению он всегда является "удаленным" хранилищем. Если бы у злоумышленника был доступ к локальному хранилищу, у него были бы намного более простые способы испортить вас.
    Таким образом, в этом случае коллизия совершенно не проблема: вы получите "плохой" репозиторий, который отличается от того, что предполагал злоумышленник, но, поскольку вы никогда не будете использовать его сталкивающийся объект, он буквально не отличается от злоумышленник просто не обнаружил коллизия, а просто использовал объект, который у вас уже был (т.е. он на 100% эквивалентен "тривиальному" столкновению идентичного файла, генерирующего тот же SHA1).

Вопрос об использовании SHA-256 регулярно упоминается, но пока не решен (2012).
Примечание: начиная с 2018 года и Git 2.19, код подвергается рефакторингу для использования SHA-256.


Примечание (юмор): вы можете принудительно зафиксировать конкретный префикс SHA1 с помощью проекта gitbrute от Брэда Фицпатрика (bradfitz).

gitbrute brute-форсирует пару временных меток "автор + коммиттер", так что результирующий git commit имеет желаемый префикс.

Пример: https://github.com/bradfitz/deadbeef


Даниэль Динниес отмечает в комментариях 7.1 Git Tools - Выбор редакции, который включает в себя:

Существует более высокая вероятность того, что каждый член вашей команды программистов будет атакован и убит волками в несвязанных инцидентах в одну и ту же ночь.


Даже совсем недавно (февраль 2017 года) shattered.io продемонстрировал возможность создания столкновения SHA1:
(см. намного больше в моем отдельном ответе, включая пост Google+ Линуса Торвальдса)

  • а/все еще требуется более 9 223 372 036 854 775 808 вычислений SHA1. Это заняло эквивалентную вычислительную мощность как 6500 лет вычислений с одним ЦП и 110 лет вычислений с одним ГП.
  • b/подделал бы один файл (с тем же SHA1), но с дополнительным ограничением его содержимое и размер дали бы идентичный SHA1 (коллизия только с одним содержимым недостаточно): см. " Как вычисляется git-хеш? "): BLA- объект SHA1 вычисляется на основе содержимого и размера.

См. " Время жизни криптографических хеш-функций " от Валери Аниты Авроры для получения дополнительной информации.
На этой странице она отмечает:

Google потратил 6500 лет CPU и 110 лет GPU, чтобы убедить всех, что нам нужно прекратить использовать SHA-1 для критически важных приложений.
Кроме того, потому что это было круто

Смотрите больше в моем отдельном ответе ниже.

Ответ 3

Согласно Pro Git:

Если вам удастся передать объект, хэш которого совпадает с значением SHA-1 в качестве предыдущего объекта в вашем репозитории, Git увидит предыдущий объект уже в вашей базе данных Git и предположим, что он уже написан. Если вы попытаетесь снова проверить этот объект в какой-то момент, вы всегда получите данные первого объекта.

Таким образом, это не подведет, но это не спасет ваш новый объект.
Я не знаю, как это выглядело бы в командной строке, но это, конечно, было бы непонятным.

Немного дальше, эта же ссылка пытается проиллюстрировать вероятность такого столкновения:

Вот пример, чтобы дать вам представление о том, что потребуется, чтобы получить столкновение SHA-1. Если все 6,5 миллиарда людей на Земле программировали, и каждую секунду каждый из них создавал код, эквивалентный всей истории ядра Linux (1 миллион объектов Git) и вставляя его в один огромный репозиторий Git, он потребуется 5 лет, пока этот репозиторий не будет содержать достаточно объектов, чтобы иметь 50% вероятность единственного столкновения объекта SHA-1. Существует большая вероятность того, что каждый член вашей команды разработчиков будет атакован и убит волками в несвязанных инцидентах в ту же ночь.

Ответ 4

Чтобы добавить к моему предыдущему ответу от 2012 года, теперь есть (февраль 2017 года, пять лет спустя) пример фактического столкновения SHA-1 с shattered.io, где вы можете создать два сталкивающихся PDF файла: получить SHA- 1 цифровая подпись на первом файле PDF, которая также может использоваться как действительная подпись на втором файле PDF.
См. Также " На пороге смерти в течение многих лет широко используемая функция SHA1 теперь не работает ", и эта иллюстрация.

Обновление от 26 февраля: Линус подтвердил следующие пункты в Google+ сообщении:

(1) Прежде всего - небо не падает. Существует большая разница между использованием криптографического хэша для таких вещей, как подпись безопасности, и его использованием для генерации "идентификатора контента" для системы с адресным контентом, такой как git.

(2) Во-вторых, природа этой конкретной атаки SHA1 означает, что на самом деле довольно легко принять меры против нее, и уже было два набора патчей для этого средства защиты.

(3) И, наконец, на самом деле существует довольно простой переход к другому хешу, который не сломает мир - или даже к старым git-репозиториям.

Относительно этого перехода см. Git 2.16 Q1 2018, в котором добавлена структура, представляющая алгоритм хеширования. Осуществление этого перехода началось.

Начиная Git 2.19 (Q3 2018), Git выбрал SHA-256 в качестве NewHash и в настоящее время интегрирует его в код (то есть SHA1 по-прежнему используется по умолчанию (Q2 2019, Git 2.21), но SHA2 будет преемником)


Оригинальный ответ (25 февраля) Но:

Джои Хесс пробует эти PDF в репозитории Git, и он обнаружил:

Это включает в себя два файла с одинаковым SHA и размером, которые получают разные BLOB-объекты благодаря тому, как git добавляет заголовок к содержимому.

[email protected]:~/tmp/supercollider>sha1sum  bad.pdf good.pdf 
d00bbe65d80f6d53d5c15da7c6b4f0a655c5a86a  bad.pdf
d00bbe65d80f6d53d5c15da7c6b4f0a655c5a86a  good.pdf
[email protected]:~/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 символов (вроде как мы уже сокращаем вещи в много ситуаций).
Таким образом, инструменты вокруг git даже не видят изменения, если они не переданы в каком-то специальном --full-hash " --full-hash " (или " --abbrev=64 ", или как угодно - по умолчанию мы сокращаем до 40).

Тем не менее, план перехода (от SHA1 к другой хэш-функции) все еще будет сложным, но активно изучается.
Новообращенного к object_id кампании является в процессе:


Обновление 20 марта: GitHub подробно описывает возможную атаку и ее защиту:

Именам SHA-1 можно присвоить доверие с помощью различных механизмов. Например, Git позволяет криптографически подписывать коммит или тэг. При этом подписывается только сам объект фиксации или тега, который, в свою очередь, указывает на другие объекты, содержащие фактические данные файла, используя их имена SHA-1. Коллизия в этих объектах может привести к подписи, которая кажется действительной, но которая указывает на данные, отличные от предполагаемых подписывающим лицом. В такой атаке подписывающий видит только одну половину столкновения, а жертва видит вторую половину.

Степень защиты:

Недавняя атака использует специальные методы для использования слабых мест в алгоритме SHA-1, которые обнаруживают коллизия за гораздо меньшее время. Эти методы оставляют шаблон в байтах, который может быть обнаружен при вычислении SHA-1 любой половины пары столкновения.

GitHub.com теперь выполняет это обнаружение для каждого вычисляемого им SHA-1 и прерывает операцию, если есть доказательства того, что объект является половиной конфликтующей пары. Это не позволяет злоумышленникам использовать GitHub, чтобы убедить проект принять "невинную" половину их столкновения, а также не позволяет им размещать вредоносную половину.

Смотрите " sha1collisiondetection " Марка Стивенса


Снова, с Q1 2018 Git 2.16, добавляющего структуру, представляющую алгоритм хеширования, началась реализация перехода к новому хешу.
Как упомянуто выше, новый поддерживаемый Hash будет SHA-256.

Ответ 5

Я думаю, что криптографы будут праздновать.

Цитата из Статья в Википедии о SHA-1:

В феврале 2005 года было объявлено нападение Xiaoyun Wang, Yiqun Lisa Yin и Hongbo Yu. Атаки могут обнаруживать столкновения в полной версии SHA-1, требующие менее 2 ^ 69 операций. (Для поиска грубой силы потребуется 2 ^ 80 операций.)

Ответ 6

Существует несколько различных моделей атак для хэшей, таких как SHA-1, но обычно обсуждаются столкновения, в том числе Marc Stevens HashClash инструмент.

"С 2012 года наиболее эффективная атака на SHA-1 считается быть одним из Марком Стивенсом [34] с приблизительной стоимостью 2,77 млн. долл. США сломать одно значение хэширования, арендуя мощность процессора от облачных серверов" .

Как указывали люди, вы можете заставить хэш-столкновение с git, но это не заменит существующие объекты в другом репозитории. Я бы предположил, что даже git push -f --no-thin не будет перезаписывать существующие объекты, но не на 100% уверен.

Тем не менее, если вы взламываете удаленный репозиторий, вы можете сделать свой ложный объект более старым, возможно, внедрить взломанный код в проект с открытым исходным кодом на github или аналогичный. Если бы вы были осторожны, возможно, вы могли бы ввести взломанную версию, которую загружали бы новые пользователи.

Я подозреваю, однако, что многие вещи, которые разработчики проекта могли бы сделать, могли либо разоблачить, либо случайно уничтожить ваш многомиллионный взлом. В частности, много денег, если какой-либо разработчик, которого вы не взломали, когда-либо запускает вышеупомянутый git push --no-thin после изменения файлов, иногда даже без --no-thin в зависимости.