Кратко

  • Компрометация Bash uploader в Codecov в 2021 году стала проверкой подотчётности за утечку секретов CI: в собственном обновлении безопасности Codecov говорилось, что третья сторона изменила Bash uploader и потенциально могла экспортировать переменные окружения и данные о Git-удалённых репозиториях из CI-сред клиентов.
  • В разборе инцидента Codecov позднее сообщил, что атакующий извлёк HMAC-ключ сервисного аккаунта Google Cloud Storage из промежуточного слоя публичного self-hosted Docker-образа и использовал этот ключ для изменения Bash uploader, который раздавался конечным пользователям.
  • Суть вопроса подотчётности не в том, что пользователям следовало проверять контрольные суммы. Codecov контролировал путь распространения загрузчика, процесс сборки публичного Docker-образа, мониторинг размещённого скрипта, уведомление клиентов и план перехода от модели доверия «curl | bash» к иной схеме.
  • Клиенты определяли, какие секреты доступны CI-джобам, какой способ загрузки использовался, применялась ли проверка контрольных сумм и насколько быстро можно было ротировать учётные данные после уведомления. Эти клиентские меры не снимали с провайдера ответственность за целостность скрипта.
  • В этой статье обновление безопасности и разбор инцидента Codecov рассматриваются как первичные источники, ответные действия клиентов — как прямые свидетельства последствий, а документация NIST, CISA, SLSA, Sigstore и систем CI — как словарь технических мер контроля. Статья не утверждает, что у неё есть доступ к материалам правоохранительных органов, закрытым спискам клиентов или полным журналам инфраструктуры атакующих.

Почему этот случай попал в досье о рисках и подотчётности

Codecov должен находиться в досье о рисках и подотчётности, потому что компрометация Bash uploader в 2021 году превратила рутинный шаг загрузки данных о покрытии в возможный путь утечки секретов CI. В обновлении безопасности от 15 апреля 2021 года (источник: about.codecov.io) Codecov сообщил, что 1 апреля узнал о том, что кто-то получил несанкционированный доступ к скрипту Bash uploader и изменил его без разрешения. Компания заявила, что злоумышленник получил доступ из-за ошибки в процессе создания Docker-образа Codecov, которая позволила извлечь учётные данные, необходимые для изменения загрузчика.

Codecov сообщил, что начиная с 31 января 2021 года периодически вносились несанкционированные изменения, которые потенциально могли экспортировать информацию, хранящуюся в средах непрерывной интеграции клиентов, на сторонний сервер за пределами инфраструктуры Codecov.

Такое публичное описание выводит дело за рамки скомпрометированного скрипта вендора. Bash uploader выполнялся внутри CI-джоб клиентов, как правило после завершения тестов и до загрузки данных о покрытии. CI-джобы могут содержать URL-адреса репозиториев, метаданные сборки, токены развёртывания, учётные данные для публикации пакетов, облачные учётные данные, ключи подписи, URL-адреса баз данных, секреты вебхуков, персональные токены доступа и переменные окружения, используемые автоматизацией тестирования и релизов.

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

Разбор инцидента Codecov (источник: about.codecov.io) уточнил картину контроля. В нём говорилось, что злоумышленник выбрал целью Bash uploader и использовал его для доставки вредоносной нагрузки пользователям Bash uploader, GitHub Action от Codecov, CircleCI Orb от Codecov и Bitrise Step от Codecov. Сообщалось, что атакующий извлёк HMAC-ключ сервисного аккаунта Google Cloud Storage из промежуточного слоя публичного self-hosted Docker-образа Codecov и использовал этот ключ для изменения Bash uploader в Google Cloud Storage.

Там также сообщалось, что один из клиентов обнаружил инцидент после проверки SHASUM: он заметил расхождение между хэшем, опубликованным на GitHub, и вычисленным хэшем скачанного файла загрузчика.

Эти факты помещают подотчётность в общую, но неравную систему. Codecov контролировал размещённый загрузчик, путь записи в Google Cloud Storage, процесс сборки self-hosted Docker-образа, ротацию ключей после инцидента, уведомление клиентов и переход к новому загрузчику. Клиенты определяли, какие переменные CI присутствовали при запуске загрузчика, загружали ли они скрипт напрямую, выполнялась ли проверка контрольных сумм и насколько быстро можно было ротировать раскрытые секреты. Провайдеры CI отвечали за часть функций маскирования секретов, изоляции джоб и журналирования.

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

Событие вписывается в экономику инструментов разработчика: ценностное предложение Codecov состояло в максимально простой передаче данных о покрытии из множества CI-систем. Архивный репозиторий Codecov Bash uploader (источник: github.com) прямо показывает эту модель удобства: клиенты могли загружать и выполнять удалённый скрипт, использовать один и тот же загрузчик в разных CI-провайдерах и при желании проверять SHASUM. Удобство было двигателем внедрения. Но когда доверенный удалённый скрипт стал изменяемым из-под контроля атакующего, скрытая цена проявилась в виде экстренной инвентаризации учётных данных, их ротации, ревизии репозиториев, уведомления клиентов и реагирования на инцидент.

Bash uploader создал инверсию доверия внутри CI

Загрузчики данных о покрытии легко недооценить. Они появляются в конце тестовой джобы, после того как основной код приложения уже выполнен. Из-за этого они могут казаться менее рискованными, чем инструменты сборки, команды развёртывания или шаги публикации пакетов. На деле загрузчик данных о покрытии видит ту же среду, что и вызвавшая его джоба, если только конвейер сознательно её не изолирует. Если в джобе есть облачные учётные данные, API-токены, доступ к репозиторию, секреты реестра пакетов или сервисные ключи, процесс загрузчика может их прочитать.

Собственная документация Codecov по Bash uploader (источник: docs.codecov.com) и инструкции архивного репозитория показывают, почему это важно. Загрузчик был спроектирован для определения настроек CI, сбора отчётов и отправки данных о покрытии. В том же репозитории описывалась процедура проверки с помощью файлов SHASUM, но в разборе инцидента Codecov признал, что до инцидента эта процедура не была полностью и должным образом задокументирована. В обновлении безопасности также говорилось, что клиенты, которые сверяли контрольные суммы перед использованием Bash uploader, могли не пострадать.

Это показательная граница: проверка целостности была действенной защитой, но модель распространения не делала её обязательной.

Инверсия доверия проста. Клиенты доверяли скрипту Codecov, потому что он был зависимостью в рабочем процессе, измеряющем покрытие тестами. Затем скрипт выполнялся внутри управляемой клиентом CI-среды, которая могла содержать куда больше полномочий, чем нужно Codecov для получения отчёта о покрытии. По сути, клиент давал инструменту отчётности позицию, с которой тот мог наблюдать за секретами развёртывания или экспортировать их, будь инструмент изменён. Это не значит, что каждый клиент потерял критически важные секреты. Это значит, что радиус поражения зависел от архитектуры CI-среды каждого клиента, а не только от границы сервиса Codecov.

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

Затем клиентам предстояло выяснить, какие секреты присутствовали в их джобах, насколько они чувствительны, к каким системам открывали доступ и прослеживается ли их последующее неправомерное использование в журналах.

Современная документация по CI укрепляет эту модель распределённого риска. Руководство по усилению безопасности GitHub Actions (источник: docs.github.com) рассматривает секреты, разрешения токенов, сторонние действия и риски внедрения скриптов. Документация GitLab о переменных CI/CD (источник: docs.gitlab.com) и документация CircleCI о переменных окружения (источник: circleci.com) показывают, как CI-системы намеренно предоставляют секреты джобам в контролируемых условиях. Эти документы — не выводы о расследовании инцидента Codecov. Они описывают среду, в которой компрометация Codecov стала серьёзной: CI-джобы — это привилегированные зоны автоматизации, а не нейтральные терминалы.

Сроки обнаружения заставили клиентов нести неопределённость

Сроки обнаружения — центральный вопрос подотчётности, потому что клиенты не могли сразу узнать масштаб. Codecov сообщил, что узнал о проблеме 1 апреля 2021 года после того, как клиент, проводивший проверку SHASUM, сообщил о расхождении. Публичное раскрытие 15 апреля последовало после расследования, форензики и согласований. В обновлении безопасности говорилось, что затронутый период начался 31 января и что пострадавшие пользователи были уведомлены по электронной почте и через уведомления в приложении.

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

Здесь нужно удерживать вместе два соображения справедливости. Первое: реагирование на инцидент действительно требует расследования. Провайдер, который раскрывает информацию, не разобравшись в базовых фактах, может ввести клиентов в заблуждение и спровоцировать неверные меры устранения. В разборе инцидента Codecov говорилось, что компания координировала расследование с федеральными правоохранительными органами и агентствами по кибербезопасности. Второе: у клиентов с раскрытыми секретами CI свои сроки.

Если облачное учётное данное, токен пакетов, ключ развёртывания репозитория или ключ подписи был доступен джобе в феврале, клиенту может потребоваться немедленно его ротировать, изучить журналы аудита и оценить последствия.

Чем дольше длится неопределённость, тем сложнее понять, использовалось ли учётное данное.

Поэтому досье подотчётности должно фиксировать, что клиенты знали 1 апреля, 15 апреля и 29 апреля. Знали ли они, какие репозитории использовали затронутые загрузчики? Знали ли они, загружали ли они загрузчик напрямую в период компрометации? Знали ли они, какие переменные окружения потенциально были получены? Знали ли они, какие именно секреты присутствовали в этих джобах? Знали ли они, проводилась ли проверка контрольных сумм? Хватало ли им журналов, чтобы изучить возможные злоупотребления? От этих вопросов зависит, можно ли было действовать на основании уведомления.

Страница предупреждений CISA о Codecov (источник: cisa.gov) сочла уведомление вендора достаточно важным, чтобы распространить его. Это полезное публичное свидетельство того, что инцидент касался цепочки поставок ПО и устранения последствий клиентами, а не только внутренней ситуации в Codecov. Полезна здесь и система NIST Secure Software Development Framework (источник: csrc.nist.gov): она рассматривает сторонние программные компоненты, среды сборки и реагирование на уязвимости как элементы контроля жизненного цикла. Компрометация Codecov оказалась на пересечении всех трёх.

История обнаружения показывает и ценность, и слабость проверки на стороне клиента. Клиент обнаружил расхождение, потому что сверил загруженный Bash uploader с ожидаемым SHASUM. Это сильный сигнал, что проверка работает. Но это слабый контроль, если его выполняют лишь необычно осторожные клиенты. Более надёжный подход — сделать выполнение неподписанного или непроверяемого кода исключением, а не нормой. В разборе инцидента Codecov сообщил, что новый загрузчик будет выпускаться как подписанный исполняемый файл с возможностью проверки по SHASUM и что отказ от Bash uploader станет частью корректирующих мер.

Анонс нового загрузчика (источник: about.codecov.io) именно поэтому относится к досье об устранении последствий.

Ротация секретов оказалась главной нагрузкой на клиентов

Как только компрометация стала публичной, операционная нагрузка быстро легла на клиентов. Codecov рекомендовал пострадавшим пользователям определить ключи и токены, попавшие в CI-среды, отозвать чувствительные учётные данные, выпустить замены и провести аудит использования токенов. Это звучит просто, пока не попробуешь применить к реальной софтверной организации.

Одна CI-джоба может содержать токены реестра пакетов, учётные данные облачного провайдера, ключи развёртывания, секреты репозитория артефактов, URL-адреса баз данных для интеграционных тестов, пароли тестовых аккаунтов, вебхуки Slack или PagerDuty, пароли реестра Docker, учётные данные состояния Terraform, материалы для подписи или персональные токены доступа. Эти секреты могут переиспользоваться в разных репозиториях или наследоваться из организационных настроек CI.

Задача на стороне клиента была не только в ротации. Главным было картирование. Какие репозитории использовали затронутый загрузчик? Какие джобы выполнялись в затронутые периоды? Какие переменные окружения присутствовали в этих джобах? Маскировались ли секреты в журналах, но оставались ли доступны процессу? Какие облачные аккаунты, реестры пакетов, хостинги кода и внутренние сервисы принимали эти учётные данные? В каких системах были журналы аудита? Как долго они хранились? Могла ли организация доказать отсутствие злоупотреблений или ей оставалась только защитная ротация?

Поэтому стоимость инцидента измерялась не только подтверждёнными злоупотреблениями, но и трудом по реагированию.

Публичные ответы самих клиентов показывают, почему это важно. В публичном уведомлении HashiCorp (источник: discuss.hashicorp.com) описывалось раскрытие закрытого GPG-ключа, использовавшегося для подписи релизов, и процесс его отзыва и замены. В ответе Twilio (источник: twilio.com) описывались расследование и оценка влияния на клиентов. В ответе Rapid7 (источник: rapid7.com) описывались собственный анализ и устранение последствий. В отчёте Mercari об инциденте (источник: about.mercari.com) описывалось раскрытие данных клиентов и сотрудников, связанное с инцидентом Codecov. Это не доказательство того, что все клиенты Codecov столкнулись с одинаковыми последствиями.

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

Пример HashiCorp особенно поучителен, потому что ключи подписи — не обычные API-токены. Ключ подписи влияет на подлинность ПО. Его замена требует отзыва, распространения нового доверия, коммуникации с клиентами и времени. Это не значит, что Codecov напрямую скомпрометировал каждый подписанный артефакт. Это значит, что утечка секретов CI может дотянуться до цепочки поставок ПО, если раскрытый материал включает ключи, используемые для подтверждения подлинности или распространения ПО. Радиус поражения зависит от типа секрета, его использования, срока жизни и доказательств отзыва.

Пример Mercari иллюстрирует ещё одну границу. Инцидент Codecov был компрометацией инструмента вендора, но раскрытие данных клиентов может проявиться в собственной среде клиента — в зависимости от секретов и систем, доступных затронутым CI-джобам. Поэтому подотчётность провайдера и подотчётность клиента нельзя развести по аккуратным коробкам. Codecov должен был восстановить целостность загрузчика и рассказать клиентам, что произошло. Клиенты должны были определить, какие секреты присутствовали и до чего эти секреты могли дотянуться.

Целостность распространения стала центральным пунктом устранения последствий

Центральным пунктом устранения последствий после инцидента Codecov стала целостность распространения. Модель Bash uploader побуждала клиентов загружать исполняемый код во время выполнения джобы. Эта модель упрощала обновления и расширяла поддержку языков, но при этом делала размещённый скрипт целью высокой ценности. Если атакующий мог изменить скрипт в хранилище, каждая непроверенная загрузка на стороне клиента могла унаследовать вредоносное изменение. Поэтому провайдер должен был показать, что изменились права на модификацию, подпись, контрольные суммы, мониторинг, сборка образов и процедуры релизов.

В разборе инцидента Codecov перечислялись несколько корректирующих мер: отзыв похищенного ключа, аудит и ротация производственных ключей, перевод публичных Docker-образов на сборки squash или multistage, запуск нового загрузчика, мониторинг соответствующих объектов Google Cloud Storage, улучшение документации по проверке подписи и SHASUM, изменение политики генерации и ротации ключей, усиление реагирования на инциденты и выделение отдельной функции безопасности. Каждая мера затрагивала свой участок цепочки. Исправление Docker-образа закрывало утечку секретов через слои образа. Ротация ключей устраняла непосредственный путь по учётным данным.

Мониторинг закрывал будущие несанкционированные изменения. Новый загрузчик менял конструкцию распространения и проверки.

Слои Docker-образа — важный пункт доказательств. Codecov сообщил, что атакующий извлёк HMAC-ключ из промежуточного слоя публичного self-hosted Docker-образа. Собственная документация Docker о multistage-сборках (источник: docs.docker.com) объясняет, почему артефакты сборки и промежуточные материалы нужно исключать из финальных образов. Поэтому корректирующая мера Codecov — squash или multistage-сборки — была напрямую связана с классом первопричины. Секрет в слое образа не защищён только потому, что итоговый контейнер выглядит чистым.

Помогает и более широкая терминология цепочек поставок ПО. Фреймворк SLSA (источник: slsa.dev) и Sigstore (источник: sigstore.dev) рассматривают целостность, происхождение, подпись и проверяемость программных артефактов. Это не ретроактивные проверки соответствия для загрузчика Codecov 2021 года. Они объясняют, почему изменяемый размещённый скрипт — более слабая модель распространения, чем подписанный, версионированный и проверяемый артефакт с подтверждённым происхождением. OpenSSF Scorecard (источник: scorecard.dev) аналогично рассматривает гигиену зависимостей и репозиториев как измеримые меры контроля цепочки поставок. Важно не то, что какой-то один фреймворк предотвратил бы всю цепочку инцидента. Важно, что целостность артефактов должна быть спроектирована так, чтобы клиентам не приходилось обнаруживать подмену вручную.

Текущая документация загрузчика Codecov (источник: docs.codecov.com) и репозиторий загрузчика (источник: github.com) показывают направление миграции от старой модели, основанной только на Bash. Репозиторий GitHub Action от Codecov (источник: github.com) остаётся актуальным, поскольку многие пользователи работали с Codecov через интеграции в платформах, а не через прямой вызов Bash. Инцидент показал, что обёртки, орбы и шаги могут наследовать границу доверия общего базового загрузчика. Клиенты могут думать, что используют нативную интеграцию CI, но риск по-прежнему может идти через тот же удалённый скрипт или бинарный файл.

Провайдеры CI и клиенты по-прежнему должны были сокращать радиус поражения

Устранение последствий на стороне провайдера было необходимо, но сокращение радиуса поражения на стороне клиента оставалось критически важным. CI-среда не должна раскрывать секреты, которые не нужны джобе. Шагу загрузки данных о покрытии, как правило, достаточно прав, чтобы прочитать файлы покрытия и пройти аутентификацию в Codecov. Ему не нужны широкие облачные учётные данные для развёртывания, доступ к производственным базам данных, полномочия публикации пакетов или долгоживущие персональные токены, если только та же джоба не совмещает несвязанные функции. Принцип минимальных привилегий в CI — не абстракция.

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

GitHub Actions, GitLab CI, CircleCI, Buildkite, Jenkins и другие CI-системы дают организациям способы ограничивать переменные, разделять джобы, маскировать секреты, ограничивать доступ по веткам, требовать защищённые среды и ограничивать разрешения токенов. Но эти меры полезны, только если архитектура конвейера их использует. Монолитная джоба, которая запускает тесты, собирает артефакты, развёртывает инфраструктуру, подписывает релизы, публикует пакеты и загружает данные о покрытии, создаёт подверженность общему режиму отказа.

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

Поэтому событие Codecov относится и к автоматизации безопасности, и к экономике инструментов разработчика. Автоматизация призвана снижать ручные риски. Но автоматизация может и скрывать допущения о доверии. YAML-файл могли скопировать годы назад. Команда загрузчика может выполняться в десятках репозиториев. Секрет уровня организации может внедряться в каждую джобу, потому что так проще, чем ограничивать его по проектам. Ротированный токен может ломать конвейеры, если непонятно, кто им владеет. Инцидент заставил команды провести аудит автоматизации, ставшей фоновой инфраструктурой.

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

Именно поэтому публичные уведомления клиентов различались. Одни организации после расследования не нашли доказательств несанкционированного доступа. Другие ротировали ключи или сообщили о более конкретном раскрытии. Это различие не противоречиво. Оно отражает тот факт, что скомпрометированный загрузчик Codecov создал потенциальный механизм выноса данных, а последствия зависели от каждой CI-среды. В подотчётной записи потенциальная подверженность, подтверждённый доступ к секретам, подтверждённое злоупотребление и влияние на данные клиентов должны вестись в отдельных категориях.

Границы доказательств и дисциплина без преувеличений

Открытые доказательства поддерживают сильный вывод, но не безграничные утверждения. Они подтверждают, что Bash uploader Codecov был изменён без авторизации, что это изменение могло экспортировать переменные окружения и данные о Git-удалённых репозиториях из CI-сред, что затронутый период начался 31 января и завершился устранением последствий 1 апреля 2021 года, что расхождение контрольных сумм у клиента помогло обнаружить проблему, что слой публичного Docker-образа раскрыл учётное данное, использованное для изменения загрузчика, и что Codecov рекомендовал пострадавшим пользователям ротировать учётные данные.

Они подтверждают, что некоторые клиенты публично сообщили о собственном устранении последствий или раскрытии.

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

Разница между возможной подверженностью и подтверждённым злоупотреблением важна для клиентов и опосредованных пользователей. В обновлении безопасности Codecov говорилось об учётных данных, токенах, ключах, сервисах, хранилищах данных, коде приложений и данных о Git-удалённых репозиториях, которые потенциально могли быть затронуты, если были доступны загрузчику. Эта потенциальная возможность была достаточно весомой, чтобы оправдать широкие рекомендации по ротации. Но потенциальная возможность — не то же самое, что доказательство последующего доступа.

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

Сроки раскрытия — ещё одна граница. Codecov публично раскрыл информацию 15 апреля, обнаружив событие 1 апреля. Компания объяснила, что проводит расследование с экспертами по форензике и координирует действия с властями. Клиенты по-прежнему вправе спрашивать, было ли возможно адресное раннее предупреждение, следовало ли уведомить клиентов с высоким риском раньше и было ли достаточно быстрым дополнительное раскрытие 29 апреля. Это законные вопросы подотчётности. Без дополнительных доказательств они не являются признаком недобросовестности.

Статья также не должна рассматривать проверку контрольных сумм как полную передачу ответственности. Codecov документировал проверку SHASUM, и именно клиент, использовавший этот контроль, обнаружил проблему. Этот факт не означает, что каждый клиент, не внедривший необязательную процедуру, был небрежен. Если обычный способ использования продукта заключался в загрузке «живого» удалённого скрипта, провайдер должен был принять на себя риск того, что многие клиенты пойдут лёгким путём. Чем бесшовнее интеграция, тем больше ответственность провайдера встроить проверку целостности в путь по умолчанию.

Долговременный критерий подотчётности — доказательство восстановленного доверия

Долговременный критерий подотчётности — превратили ли Codecov и его клиенты инцидент в доказательство восстановленного доверия. Для Codecov доказательство означало отзыв похищенного ключа, ротацию внутренних учётных данных, удаление раскрытых секретов из слоёв образов, изменение процессов сборки Docker-образов, мониторинг хранилища размещённого скрипта, документирование проверки, выпуск подписанного и проверяемого загрузчика на замену и улучшение реагирования на инциденты.

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

Ключевой вопрос устранения последствий — модель распространения по умолчанию. Если клиенты по-прежнему массово загружают и выполняют изменяемый код без встроенной проверки, тот же класс риска сохраняется. Если загрузчики подписаны, версионированы, закреплены по версиям, проверяются по контрольным суммам и мониторятся, риск снижается. Если CI-джобы изолируют загрузку покрытия от полномочий развёртывания, будущей компрометации загрузчика будет меньше что красть. Если секрет-менеджеры выпускают короткоживущие учётные данные вместо долгоживущих токенов, экстренная ротация упрощается.

Если журналы аудита хранятся достаточно долго, клиенты могут отличить предосторожность от подтверждённого злоупотребления.

Команды закупок и управления должны извлечь урок и из этого случая. Инструмент разработчика, выполняемый в CI, — не безобидное дополнительное средство наблюдения. Это выполнение кода внутри среды автоматизации, где могут находиться ценные секреты. При проверке вендоров следует спрашивать, как распространяются загрузчики и действия, подписаны ли артефакты, как обнаруживается компрометация, как уведомляются клиенты, какую телеметрию вендор может предоставить для выявления затронутых репозиториев и как вендор избегает хранения или раскрытия ключей подписи в публичных артефактах сборки.

Инцидент изменил и смысл понятия «автоматизация безопасности». Автоматизация безопасна не потому, что она автоматизирована. Она безопасна, когда у неё ограниченные полномочия, проверяемые входные данные, воспроизводимые артефакты, доступные для аудита журналы и быстрый отзыв доступа. Компрометация Bash uploader в Codecov показала, что небольшой шаг автоматизации может стать большой границей доверия, если он выполняется там, где живут секреты.

Финальный урок практичен. Данные о покрытии важны, но загрузка покрытия не должна делить комнату со всеми учётными данными компании. Поставщик телеметрии для разработчиков обязан доказывать целостность кода, который он просит клиентов выполнять. Клиенты должны проектировать CI так, чтобы сторонние инструменты видели только необходимое. Когда любая из сторон полагается на удобство без доказательств, счёт приходит в виде экстренной ротации секретов, уведомлений клиентов и неопределённости в цепочке поставок. Codecov сделал этот счёт видимым.

Загрузчик был небольшим, но его среда выполнения — обширной

Одна из причин, по которой инцидент Codecov остаётся важным, — то, что скомпрометированный объект был операционно мал. Это не полноценный хостинг исходного кода, не облачная консоль, не провайдер идентификации и не реестр пакетов. Это был загрузчик данных о покрытии. Но среда выполнения загрузчика могла быть большой, потому что CI-джобы часто собирают полномочия сразу из многих систем.

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

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

Именно поэтому принцип минимальных привилегий в CI нужно реализовывать на уровне джоб и шагов, а не только на уровне организации. Секрет, уместный для развёртывания, не должен внедряться в джобу, которая занимается только покрытием. Токен публикации пакетов не должен быть доступен на шаге модульного тестирования, загружающем покрытие. Облачное учётное данное для интеграционных тестов должно быть короткоживущим и ограниченным тестовыми ресурсами. Токены репозиториев должны иметь минимальные разрешения. Секреты защищённых веток не должны раскрываться в контекстах недоверенных pull request.

Компрометация Codecov не изобрела эти правила, но дала конкретный повод их соблюдать.

Инцидент также показывает, почему маскирования секретов недостаточно. CI-платформы часто маскируют секреты в журналах — это полезно, но маскирование не мешает процессу прочитать переменную окружения и отправить её куда-то ещё. Маскирование защищает людей, читающих журналы. Оно не превращает секрет с высокими полномочиями в секрет с низкими полномочиями. Если сторонний скрипт выполняется в том же процессе, что и секрет, контроль уже перешёл от секретности в журналах к доверию к коду. Это гораздо более сильное допущение.

Более надёжная архитектура выделяет загрузку покрытия в ограниченную стадию. Эта стадия получает только файлы покрытия и токен, необходимый для аутентификации в сервисе покрытия. Она запускает закреплённую версию загрузчика или подписанный проверенный бинарный файл. У неё нет учётных данных развёртывания, производственных облачных ключей, токенов публикации пакетов, долгоживущих персональных токенов доступа и доступа к несвязанным результатам джоб. Если загрузчик впоследствии будет скомпрометирован, атакующий увидит узкую среду. Инцидент становится проблемой целостности вендора, а не событием ротации секретов масштаба всего предприятия.

Такая архитектура помогает и расследованиям. Когда у скомпрометированного стороннего инструмента узкая среда, реагирующие быстро отвечают на вопросы о подверженности. Они знают, какой токен присутствовал, до какой системы он дотянулся, ротирован ли он и какие журналы проверять. Когда у джобы много унаследованных секретов, реагирующим приходится под давлением времени восстанавливать широкий граф. Операционные издержки инцидента Codecov отчасти возникли именно из этой неопределённости. Клиентам пришлось выяснять, что сделали видимым их собственные CI-джобы.

Проверка должна быть стандартной, наблюдаемой и будничной

Материалы по Codecov иллюстрируют и более широкий принцип: проверка ПО должна быть стандартной, наблюдаемой и будничной. Она не должна зависеть от того, что один необычно внимательный клиент заметил расхождение хэшей. Клиент, обнаруживший расхождение, оказал важную общественную услугу, но зрелый канал распространения должен делать подмену видимой для каждого потребителя или останавливать выполнение автоматически.

Это означает закреплённые версии, подписанные артефакты, независимую публикацию контрольных сумм, воспроизводимые или проверяемые сборки там, где это возможно, мониторинг неожиданных изменений объектов и понятную документацию, которой обычные команды смогут следовать, не становясь специалистами по цепочкам поставок.

Здесь нужен практический баланс. Инструменты разработчика успешны, потому что их легко внедрять. Если проверка слишком сложна, команды будут её пропускать. Если проверка опциональна и спрятана, ею воспользуются только самые сознательные в вопросах безопасности команды. Поэтому провайдер должен проектировать интеграцию по умолчанию так, чтобы безопасный путь был обычным путём. GitHub Action должен явно закреплять версии и разрешения. Бинарный загрузчик должен быть подписан, а инструкции установки — включать проверку. CI-орб или шаг не должны молча оборачивать изменяемый удалённый скрипт, не делая эту зависимость видимой.

Документация должна объяснять сценарии отказов, а не только команды счастливого пути.

Наблюдаемость важна и на стороне провайдера. В разборе инцидента Codecov сообщалось о добавлении мониторинга соответствующих объектов Google Cloud Storage. Эта категория контроля необходима. Размещённый скрипт или бакет распространения бинарных файлов должен порождать оповещения при неожиданном изменении содержимого, при использовании ключей из необычных контекстов, при изменении метаданных объектов или при отклонении путей релизов от ожидаемой автоматизации. Подпись защищает клиентов в момент выполнения. Мониторинг защищает провайдера в момент распространения. Нужны оба.

Наконец, доказательства проверки должны переживать инцидент. После компрометации клиентам нужно знать, какие версии артефактов существовали, когда они менялись, какие подписи были действительны, какие интеграционные пути загружали какой артефакт и какие репозитории выполнялись в затронутый период. Провайдер, который не может ответить на эти вопросы, оставляет клиентам выводы о подверженности на основе возможно неполных журналов. Лучшее устранение последствий после Codecov — не только более безопасный загрузчик. Это реестр доказательств распространения, позволяющий клиентам быстро определять масштаб будущих инцидентов.

Этот реестр — мост между ответственностью провайдера и действиями клиента. Codecov может сделать целостность загрузчика проверяемой. Клиенты могут сузить секреты CI и закрепить зависимости. CI-платформы могут поддерживать границы разрешений, ограничение секретов и журналы аудита. Ни одна из этих мер по отдельности недостаточна. Вместе они превращают шаг загрузки покрытия из скрытого допущения о доверии в контролируемую границу автоматизации.

Закупки должны рассматривать CI-инструменты как привилегированный код

Урок для закупок прямой: любой инструмент, выполняемый в CI, следует рассматривать как привилегированный код, даже если его бизнес-функция звучит наблюдательной. Отчёты о покрытии, статический анализ, сканирование зависимостей, загрузка артефактов, генерация заметок о релизах, публикация документации и уведомления о развёртывании могут выполняться в средах, где хранятся секреты. Анкета вендора, которая спрашивает только о том, хранит ли сервис данные клиентов, упускает более важный вопрос: какой код вендор просит клиента выполнять, откуда берётся этот код, как он проверяется и какие полномочия может наблюдать во время выполнения?

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

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

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

Такой пакет поддержки нужно протестировать до инцидента. Провайдер должен знать, какие клиентские системы могут отправлять срочные уведомления, на каких страницах документации будет размещено руководство по ротации, какие очереди поддержки будут разбирать среды высокого риска и какие журналы помогут клиентам понять, выполнялась ли интеграция в затронутый период. Без такой подготовки раскрытие становится вторым режимом отказа: провайдер может выявить технический инцидент, но клиенты всё равно потеряют время на восстановление практической карты подверженности.

А значит, подотчётность включает не только предотвращение подмены загрузчика, но и обеспечение пригодности первого часа реагирования клиента.

Поэтому этот случай — не только историческая заметка об одном скомпрометированном Bash-скрипте. Это шаблон для оценки инструментов разработчика, работающих внутри автоматизации. Безопасный вопрос больше не звучит абстрактно: «доверяем ли мы этому вендору?». Безопасный вопрос звучит так: «что мог бы прочитать этот код, контролируемый вендором, если бы он изменился завтра, и какие доказательства показали бы нам это быстро?». Инцидент Codecov сделал этот вопрос видимым для каждой команды, которая позволила удобному скрипту работать рядом с секретами производственного уровня.