[Перевод] Как уберечься от кражи репозитория (реподжекинга)

2ef6f4cdb7a947d3fbcb1cd85d1903ea.png

Реподжекинг или захват репозитория / перехват контроля над репозиторием — это особый вид атак на цепочки поставок. В этой статье поговорим о том, что это такое, каков риск и как можно себя обезопасить.

Этот тип атаки привлёк мое внимание из-за своего потенциального влияния на программное обеспечение с открытым исходным кодом. В этой статье я объясню, что такое захват репозитория и что можно сделать, чтобы оставаться в безопасности. Говоря вкратце: если вы получаете все свои программные зависимости из менеджера пакетов — например, npm или PyPI, то эта атака не может на вас повлиять. Если вы берёте зависимости напрямую с GitHub, нужно быть более осторожными, но есть простое решение — заблокировать определённый ID коммита. Я поясню, как это сделать, в нескольких наиболее распространённых сценариях.

Атаки на цепочки поставок в целом представляют собой серьёзную проблему, поскольку успешная атака может доставить вредоносное ПО большому количеству жертв. Однако вероятность того, что злоумышленнику удастся провести успешную крупномасштабную атаку на цепочку поставок с помощью одного лишь взлома репозитория, очень мала. Большинство программных зависимостей доставляется через менеджеры пакетов, поэтому наиболее вероятным вектором атаки будет загрузка вредоносного пакета в менеджер пакетов. Но такие менеджеры пакетов, как npm или PyPI, не позволят этого сделать, если у вас нет доступа к учётным данным разработчика. А если у вас есть доступ к учётным данным разработчика, то у вас уже есть возможность запустить атаку по цепочке поставок, без всякого взлома репозитория.

Что такое захват репозитория?

Это особый тип атаки на цепочку поставок программного обеспечения. Атака на цепочку поставок — это когда доверенный пакет программного обеспечения заменяется вредоносным ПО. Например, представьте себе последствия, если система «автоматического обновления» крупного производителя программного обеспечения будет взломана: это позволит злоумышленнику распространить вредоносное ПО на миллионы устройств, мгновенно взломав их все. Тот же сценарий применим, если злоумышленнику удастся подменить вредоносным ПО крупный проект с открытым исходным кодом. Например, представьте, что репозиторий kubernetes/kubernetes был заменён на вредоносное ПО. Многие разработчики регулярно загружают и собирают Kubernetes, поэтому если в систему сборки будет внедрено вредоносное ПО, то оно будет запущено на многих ноутбуках разработчиков.

Самым простым способом атаки на цепочку поставок в репозитории GitHub было бы размещение вредоносного коммита кем-то с правами на запись (то есть одним из разработчиков проекта). В качестве альтернативы кто-то может создать пул-реквест и пронести вредоносный код мимо ревьюеров. У GitHub есть множество средств защиты от такого рода атак: обязательная двухфакторная аутентификация, защищённые ветки и контроль управления доступом.

Атака вида реподжекинг может произойти, если пользователь GitHub изменит своё имя пользователя. В качестве чисто гипотетического примера рассмотрим собственный аккаунт GitHub на сайте https://github.com/github, который содержит сотни публичных репозиториев. Теперь предположим, что GitHub изменит имя учётной записи на gh1. Тогда такой репозиторий, как https://github.com/github/cmark-gfm, будет переименован в https://github.com/gh/cmark-gfm. Теперь представьте, что злоумышленнику удалось зарегистрировать новый GitHub аккаунт с новым именем пользователя github. Тогда он может создать репозиторий с именем cmark-gfm и начать рассылать вредоносное ПО разработчикам, которые всё ещё загружают свои программы с оригинального адреса.

Каков риск взлома репозитория?

GitHub использует алгоритм tombstoning, чтобы снизить риск этой атаки, навсегда удаляя определённые комбинации имени владельца и имени репозитория. Приведённый выше пример github/cmark-gfm является чисто гипотетическим, поскольку в этом случае старое имя будет автоматически удалено. Например, даже если злоумышленнику удастся зарегистрировать имя пользователя github, он всё равно не сможет создать новый репозиторий с именем cmark-gfm — поскольку эта комбинация имени владельца и имени репозитория (github/cmark-gfm) будет навсегда вычеркнута из списка. Таким образом, реподжекинг представляет риск только для репозиториев, которые находятся ниже определённого порога использования. Мы не удаляем все переименованные репозитории, потому что существует компромисс между удобством использования и безопасностью: удаление влечёт за собой потенциальное неудобство для наших пользователей, которое мы не хотим причинять, если для этого нет реальных причин, связанных с обеспечением безопасности. Именно поэтому наша политика удаления срабатывает только после того, как репозиторий соответствует определённым критериям — например, если он превысил определённое количество клонов.

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

Автоматическое перенаправление

Один из факторов, способствующих увеличению риска захвата репозитория, заключается в том, что переименованные репозитории автоматически перенаправляются. Если снова воспользоваться примером github/cmark-gfm, то github/cmark-gfm будет автоматически перенаправлен на gh/cmark-gfm. Другими словами, такие операции, как клонирование github/cmark-gfm, будут продолжать работать. Однако автоматический редирект будет удалён в тот момент, когда новый пользователь github создаст репозиторий с именем cmark-gfm. Преимущество автоматической переадресации заключается в том, что она уменьшает количество сбоев, которые может вызвать переименование;, но это также означает, что люди с гораздо меньшей вероятностью заметят, что одна из их зависимостей была переименована. Это, в свою очередь, увеличивает количество проектов, которые могут быть потенциально затронуты успешной атакой вида реподжекинг.

Менеджеры пакетов

Программное обеспечение часто распространяется через менеджеры пакетов (а не загружается напрямую с GitHub), что создаёт дополнительный уровень защиты от взлома репозитория. Примерами менеджеров пакетов являются npm (JavaScript), PyPI (Python) и crates.io (Rust). Чтобы заменить пакет в менеджере пакетов вредоносным ПО, обычно требуется получить [несанкционированный] доступ к учётной записи разработчика — например, угадав его пароль. Это отдельная проблема. Кроме того, злоумышленник, имеющий доступ к учётной записи разработчика, вероятно, может загрузить вредоносное ПО напрямую, без необходимости взлома репозитория.

Есть одна ситуация, в которой менеджер пакетов может повысить риски взлома репозитория, — это если он автоматически берёт последнюю версию с GitHub. Так произошло в мае 2022 года, когда исследователь безопасности взломал репозиторий hautelook/phpass. Packagist, менеджер пакетов для PHP, автоматически просматривает GitHub в поисках новых версий и автоматически загружает их, поэтому исследователь смог загрузить вредоносное ПО исключительно с помощью взлома репозитория. Чтобы исключить риск повторения подобной ситуации, недавно Packagist обновили.

Другие менеджеры пакетов, такие как PyPI и npm, работают иначе: новую версию пакета без предварительной успешной аутентификации в менеджере пакетов загрузить не получится. Другими словами, для загрузки вредоносного ПО на PyPI или npm недостаточно одного лишь взлома репозитория.

Загрузка программного обеспечения непосредственно с GitHub

Взлом репозитория становится более реальной проблемой, если вы или система сборки вашего проекта загружаете программное обеспечение непосредственно с GitHub, а не из менеджера пакетов. Обычно это делается по причине того, что вы используете либо GitHub Actions, либо язык программирования Go, либо субмодули git.

Все три способа позволяют напрямую ссылаться на другой репозиторий GitHub. Например, в файле рабочего процесса GitHub Actions вы можете использовать открытое действие, подобное этому:

steps:
    - uses: actions/javascript-action@v1.0.1

Это позволит загрузить и запустить код из репозитория actions/javascript-action. Аналогичным образом, в языке программирования Go часто встречается импорт кода непосредственно из других репозиториев GitHub, например, из этого:

require (                                                                                                                                                                                                             
        github.com/google/uuid v1.3.0
)

Самый простой способ избежать взлома репозитория при загрузке программ непосредственно с GitHub — ссылаться на определённый ID коммита. Поскольку идентификаторы коммитов Git представляют собой хэши SHA-1, злоумышленнику придётся взломать SHA-1 (теоретически это возможно, но очень маловероятно), чтобы заменить код вредоносной программой. Если вы используете подмодули Git, то вам не стоит беспокоиться — подмодули всегда привязаны к определённому ID коммита: вам нужно быть осторожным только при обновлении подмодуля до новой версии. Если вы используете Go, то вы можете использовать файл go.sum для блокировки криптографических хэшей всех ваших зависимостей. И наконец, если вы используете GitHub Actions, вы можете использовать этот синтаксис для блокировки определённого ID коммита:

steps:
    - uses: actions/javascript-action@4be183afbd08ddadedcf09f17e8e112326894107

Использование ID репозитория для проверки на взлом репозитория

Советы в этом разделе предназначены в первую очередь для разработчиков, которые внедряют систему (например, менеджер пакетов), взаимодействующую с API GitHub. Но код очень прост, поэтому вы можете легко использовать его и для проверки собственных зависимостей. С помощью API GitHub легко проверить, был ли репозиторий переименован или заменён. Помимо имени, каждый репозиторий имеет уникальный ID, который можно получить с помощью API «Get a repository». ID — это уникальное целое число, которое не меняется, если репозиторий переименован. Взлом репозитория подразумевает замену оригинального репозитория на новый, что приводит к изменению ID репозитория. Поэтому обнаружить взлом репозитория легко: достаточно проверить, не изменился ли идентификатор. Вот пример того, как это сделать с помощью Python-библиотеки PyGithub:

from github import Github

def check_repo_id(full_name, id):
    gh = Github()
    repo = gh.get_repo(full_name)
    if repo.id != id:
        raise Exception(f'repo ID has changed to {repo.id}')
    if repo.full_name != full_name:
        raise Exception(f'repo has been renamed to {repo.full_name}')

check_repo_id("github/cmark-gfm", 75244322)

Функция check_repo_id использует API GitHub для получения идентификатора репозитория и выбрасывает исключение, если он не совпадает с ожидаемым значением. В качестве дополнительной меры предосторожности она также проверяет, не был ли репозиторий переименован.

Заключение

Взлом репозитория, или реподжекинг — это атака на цепочку поставок, которая может произойти, если пользователь GitHub решит изменить своё имя пользователя; однако GitHub использует алгоритм tombstoning для снижения риска этой атаки, навсегда удаляя имена популярных репозиториев, когда они переименовываются. Программное обеспечение часто загружается из менеджеров пакетов, а не с GitHub напрямую — это означает, что менеджер пакетов создает дополнительный уровень защиты от взлома. Если в вашем проекте есть зависимости, загружаемые напрямую с GitHub, то самый простой способ обезопасить себя — это привязка к определённому ID коммита.

Интересным новым достижением в области обеспечения безопасности цепочек поставок, которого я не успел коснуться в этой статье, является работа над проверкой подлинности сборок. Рабочие процессы GitHub теперь могут использовать OpenID Connect (OIDC) для безопасной загрузки артефактов сборки в менеджер пакетов. Рабочий процесс отправляет менеджеру пакетов токен OIDC, содержащий информацию, которую менеджер пакетов может использовать для принятия решения о публикации новой версии пакета. Например, токен включает имя репозитория и файл рабочего процесса, который был запущен. Идентификатор репозитория также включён, что позволяет предотвратить взлом (а также многие другие формы атак на цепочки поставок). Проверка подлинности сборки OIDC уже интегрирована в npm, PyPI, homebrew и RubyGems.org.

Ещё одним важным событием в области безопасности цепочек поставок является совместная работа различных организаций над фреймворком Supply-chain Levels for Software Artifacts (SLSA). Рекомендую ознакомиться с информацией на сайте: https://slsa.dev/ 

В заключение приглашаем всех желающих на открытый урок, посвящённый созданию GraphQL API с NestJS, который пройдёт уже сегодня вечером. Записаться можно на странице курса «JavaScript Developer. Professional».

Habrahabr.ru прочитано 3073 раза