Гостевая статья Новый алгоритм хеширования для Git

Система управления исходным кодом Git, как известно, построена на алгоритме хеширования SHA ‑ 1, который с годами становится все более слабой основой. SHA ‑ 1 теперь считается сломанным, и, несмотря на то, что он еще не настолько сломан, чтобы его можно было использовать для взлома репозиториев Git, пользователи все больше беспокоятся о его защищенности. Хорошей новостью является то, что работа по выводу Git за пределы SHA ‑ 1 уже ведется и постепенно начинает приносить плоды; есть версия кода, которую можно посмотреть сейчас.

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

Git часто описывается как файло-адресная файловая система, где вы можете найти объект, если знаете его контент. Это может показаться не особенно полезным, но есть несколько способов «узнать» этот контент. В частности, вы можете заменить криптографический хеш для самого контента; с этим хэшем легче работать и он обладает некоторыми другими полезными свойствами.

Git хранит несколько типов объектов, используя хеш-коды SHA ‑ 1 для их идентификации. Так, например, хэш SHA ‑ 1
Код:
drivers / block / floppy.c
в 5.6-merge-window kernel , рассчитанный Git, равен
Код:
485865fd0412e40d041e861506bb3ac11a3a91e3
. Концептуально, по крайней мере, Git будет хранить эту версию
Код:
floppy.c
в файле, используя этот хэш в качестве имени; ранние версии Git действительно делали это. Если кто-то внес изменения в
Код:
 floppy.c
, даже просто удалив лишний пробел в конце строки, результат будет иметь совершенно другой хэш SHA ‑ 1 и будет сохранен под другим именем.

Таким образом, репозиторий Git полон объектов (часто называемых «блобами») с именами SHA ‑ 1; поскольку новый создается для каждой ревизии файла, они имеют тенденцию к увеличению. В настоящее время репозиторий ядра вашего редактора содержит 8 647 655 объектов. Но BLOB-объекты не являются единственными типами объектов, хранящихся в репозитории Git.

Отдельный файловый объект содержит определенный набор содержимого, но у него нет информации о том, где этот файл появляется в иерархии хранилища. Если
Код:
floppy.c
когда-нибудь переместится в
Код:
drivers/staging
, его хеш останется прежним, поэтому его представление в базе данных объектов Git не изменится. Отслеживание того, как файлы организованы в иерархию каталогов, является задачей «древовидного» объекта. Любой данный объект дерева можно рассматривать как набор BLOB-объектов (каждый из которых, конечно, определяется своим хешем SHA ‑ 1), связанных с их расположением в дереве каталогов. Как и следовало ожидать, объект дерева имеет собственный хэш SHA ‑ 1, который используется для его хранения в хранилище.

Наконец, объект «commit» записывает состояние хранилища в определенный момент времени. В коммите содержатся некоторые метаданные (коммиттер, дата и т. Д.), А также хэш SHA ‑ 1 объекта дерева, отражающий текущее состояние репозитория. Обладая этой информацией, Git может проверить хранилище при заданном коммите, воспроизводя состояние файлов в хранилище в этот момент. Важно отметить, что коммит также содержит хэш предыдущего коммита (или несколько коммитов в случае слияния); таким образом, он записывает не только состояние хранилища, но и предыдущее состояние, что позволяет точно определить, что изменилось.

У коммитов тоже есть хэши SHA ‑ 1, и хэш предыдущего коммита (или коммитов) включается в этот расчет. Если две цепочки разработки заканчиваются одинаковым содержимым файла, полученные коммиты все равно будут иметь разные хэши. Таким образом, в отличие от некоторых других систем управления исходным кодом, Git (концептуально, по крайней мере) не записывает «дельты» от одной ревизии к другой. Таким образом, он образует своего рода блокчейн, где каждый блок содержит состояние хранилища при данном коммите.

Почему хэш имеет значение для безопасности
Компрометация kernel.org в 2011 году породила немало опасений по поводу безопасности репозитория исходного кода ядра. Если злоумышленник сможет внедрить бэкдор в код ядра, результатом может стать возможный компромисс огромного числа развернутых систем. Вредоносный код, помещенный в систему сборки ядра, может быть запущен за любым количеством корпоративных и государственных брандмауэров. Это был не приятный сценарий, но, благодаря использованию Git, он также не был особенно вероятным.

Давайте представим, что какой-то злоумышленник получил контроль над kernel.org и хочет поместить вредоносный код в
Код:
floppy.c
- что-то невероятное, например, изменение, которое заменяет случайные сектора сегментами из видео Рика Эстли, скажем. Каким-то образом это изменение должно быть включено в хранилище, чтобы оно было включено в последующие операции. Но изменение в
Код:
 floppy.c
меняет свой хэш SHA ‑ 1; это, в свою очередь, изменит каждый объект дерева, содержащий злой
Код:
floppy.c
, и каждый коммит, который включает его. Главный коммит для хранилища, безусловно, изменится, как и старые, если злоумышленник попытается сделать так, чтобы изменения произошли в далеком прошлом.

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

Целостность репозитория также защищена подписанными тегами, которые включают в себя хэш для конкретного коммита и криптографическую подпись. Цепочка хэшей, ведущая к данному тегу, не может быть изменена без аннулирования самого тега. Использование подписанных тегов не является универсальным в сообществе ядра (и редко встречается во многих других проектах), но основные выпуски ядра подписываются таким образом. Когда кто-то видит подпись Линуса Торвальдса на теге, он знает, что хранилище находится в том состоянии, которое он предполагал, когда применялся тег.

Все это зависит от силы используемого хэша. Если наш злоумышленник сможет изменить floppy.c таким образом, что его хэш SHA ‑ 1 не изменится, это изменение может остаться незамеченным. Вот почему новости о коллизиях хэш-памяти SHA ‑ 1 вызывают беспокойство; если SHA ‑ 1 нельзя доверять для обнаружения враждебных изменений, то он больше не обеспечивает целостность хранилища.

К счастью, мир еще не закончился. Все еще достаточно дорого создавать какие-либо коллизии SHA ‑ 1. Создать любую новую версию floppy.c с таким же хешем будет сложно. Злоумышленник не должен был бы просто сделать это, хотя; эта новая версия должна будет содержать желаемый враждебный код, по-прежнему функционировать как работающий драйвер гибкого диска и не выглядеть как запутанная запись конкурса кода C (по крайней мере, не больше, чем она уже делает). Создание такого зверя, вероятно, все еще невозможно. Но надпись явно на стене; время, когда SHA ‑ 1 слишком слаб для Git, быстро приближается.

Переход к более сильному хешу
Еще в первые дни Git Торвальдс не о возможности взлома SHA-1; в результате он никогда не задумывался о возможности переключения на другой хэш; SHA ‑ 1 имеет основополагающее значение для работы Git. год код Git был полон объявлений, таких как:
unsigned char sha1[20];
Другими словами, тип хэша был глубоко связан с кодом, и предполагалось, что хэши поместятся в 20-байтовый массив.

В то время разработчик Git Брайан М. Карлсон уже работал над отделением ядра Git от конкретного используемого хэша; действительно, он работал над этим с 2014 года. Было неясно, какой хеш может в конечном итоге заменить SHA ‑ 1, но было возможно создать абстрактный тип для хеш-объектов, которые бы скрывали эту деталь. На этом этапе эта работа завершена и объединена.

Решение о замене алгоритма хеширования было принято в 2018 году . Был рассмотрен ряд возможностей, но сообщество Git остановилось на SHA ‑ 256 в качестве хеша Git следующего поколения. Обязательство, закрепляющее этот выбор, ссылается на его относительно долгую историю, широкую поддержку и хорошие результаты. Сообщество также решило (и в основном реализовало) план перехода, который хорошо документирован ; большая часть того, что следует, беззастенчиво выписана из этого файла.

С алгоритмом хеширования, абстрагированным от основного кода Git, переход, на первый взгляд, относительно прост. Новая версия Git может быть создана с другим алгоритмом хеширования, а также с инструментом, который преобразует хранилище из старого хэша в новый. С помощью простой команды вроде:
Код:
git convert-repo --to-hash=sha-256 --frobnicate-blobs --climb-subtrees \
       --liability-waiver=none --use-shovels --carbon-offsets
пользователь может оставить SHA ‑ 1 (обратите внимание, что определенные параметры командной строки могут отличаться). Однако у этого плана есть только одна проблема: большинство Git-репозиториев работают не в вакууме. Такое преобразование дня флага может работать для крошечного проекта, но оно не будет работать хорошо для проекта, подобного ядру. Таким образом, Git должен иметь возможность работать с хэшами SHA ‑ 1 и SHA ‑ 256 в обозримом будущем. Это требование имеет ряд последствий, которые ощущаются во всей системе.

Одна из целей разработки перехода заключается в том, чтобы репозитории SHA ‑ 256 могли взаимодействовать с репозиториями SHA ‑ 1, управляемыми более старыми версиями Git. Если kernel.org обновится до нового формата, разработчики, работающие с более старыми версиями, все равно должны иметь возможность извлекать (и продвигать) этот сайт. Это произойдет только в том случае, если Git продолжает отслеживать хэши SHA ‑ 1 для каждого объекта в течение неопределенного времени.

Для BLOB-объектов это отслеживание будет происходить через ведение набора таблиц перевода; учитывая хеш, сгенерированный одним алгоритмом, Git сможет найти соответствующий хеш из другого. Излишне говорить, что этот поиск будет успешным только для объектов, которые на самом деле находятся в хранилище. Эти таблицы перевода будут храниться в «пакетных файлах», которые содержат большинство объектов в современном Git-хранилище. Там будет отдельная таблица для «незакрепленных объектов», которые хранятся как отдельные файлы, а не как пакеты; стоимость поиска в этой таблице считается достаточно высокой, поэтому необходимо принять меры для минимизации количества незакрепленных объектов в любом данном хранилище.

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

С этим механизмом установки Git будут совместимы во время перехода. В конце концов, все пользователи будут обновлены до версий Git с поддержкой SHA ‑ 256, после чего владельцы репозитория смогут начать отключать возможность SHA ‑ 1 и удалять таблицы перевода. Переход к этому моменту будет завершен.

Некоторые неудобные детали
Естественно, на этом пути могут быть некоторые глюки. Одна из них - это простая проблема человеческого фактора: когда пользователь вводит хеш-значение, должно ли оно интерпретироваться как SHA ‑ 1 или SHA ‑ 256? В некоторых случаях это однозначно; Хэши SHA ‑ 1 имеют ширину 160 бит, поэтому 256-битный хэш должен быть, например, SHA ‑ 256. Но может быть и более короткий хеш, поскольку хеши могут быть (и часто) сокращенными. В переходном документе описан многоэтапный процесс, в ходе которого интерпретация значений хеш-функции будет меняться, но большинство пользователей вряд ли пройдут этот процесс.

Конечно, есть способ однозначно дать хеш-значение в новом коде Git, и они могут даже смешиваться в командной строке; этот пример взят из документа перехода:
[lCODE]git --output-format=sha1 log abac87a^{sha1}..f787cac^{sha256}[/ICODE]
Для пользовательского интерфейса Git это относительно просто и лаконично, но все же можно представить, что пользователи могут уставать от него относительно быстро. Очевидное решение для такого рода усталости скобок состоит в том, чтобы полностью перевести проект на SHA ‑ 256 как можно быстрее.

Однако есть еще одна проблема: в дикой природе много хэш-значений SHA ‑ 1. В настоящее время репозиторий ядра содержит более 40 000 коммитов с тегом Fixes:; каждый из них включает хэш SHA ‑ 1. Эти значения хеш-функции также можно найти в истории отслеживания ошибок, объявлениях о выпуске, раскрытии уязвимостей и многом другом. В репозитории без совместимости с SHA ‑ 1 все эти хэши станут бессмысленными. Чтобы решить эту проблему, можно представить, что разработчики Git могут в конечном итоге добавить режим, в котором переводы для старых хешей SHA ‑ 1 остаются в хранилище, но не добавляются хеши SHA ‑ 1 для новых объектов.

Текущее состояние
Большая часть работы по реализации перехода SHA ‑ 256 была проделана, но он остается в относительно нестабильном состоянии, и большая часть его даже еще не проходит активного тестирования. В середине января Карлсон первую часть этого кода перехода, которая явно решает только часть проблемы:
Код:
Во-первых, он содержит фрагменты, необходимые для настройки репозиториев
и записи но не read_ extensions.objectFormat. Другими словами, вы можете создать репозиторий
SHA ‑ 256, но не сможете его прочитать.
Обычно считается, что ценность репозиториев только для записи относительно низкая; даже SCCS не был так ограничен. Целью Карлсона в публикации кода на данном этапе является попытка выявить какие-либо основные проблемы, которые будет сложнее изменить в ходе работы. Разработчики, которые заинтересованы в том, куда движется Git, могут захотеть поближе взглянуть на этот код; конвертировать их рабочие репозитории не рекомендуется.

Как оказалось, работа Карлсона выходит далеко за рамки того, что было выставлено на тестирование сейчас; он опубликует это, когда будет готов, но действительно любопытные люди могут увидеть это сейчас в своем репозитории GitHub . Эта работа вряд ли попадет в системы большинства пользователей Git еще какое-то время, но приятно знать, что она приближается к готовности. Разработчики Git (в частности, Карлсон) тихо работают над этим проектом в течение многих лет; мы все выиграем от этого.

Источник:
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!