Проект ЦИТадель
Можно защитить репозиторий, проверять каждый коммит и всё же выпустить скомпрометированный продукт. Между исходниками команды и запущенным артефактом стоят менеджер пакетов, прямые и транзитивные зависимости, сценарии установки, компиляторы, плагины, действия CI, базовые образы и система публикации. Каждый из этих элементов способен исполнить код или изменить результат сборки. Если атакующий захватил одно из звеньев, вредоносное изменение приходит не в обход установленного процесса, а по разрешённому каналу — как обычная зависимость, сборка или обновление.
Цепочка поставок ПО — это весь путь от исходников и внешних компонентов до артефакта, который получает пользователь или рабочая среда. Её безопасность нельзя установить одним ревью исходного кода: нужно знать, кто и что имеет право влиять на выпуск, откуда берутся входы сборки, можно ли обнаружить их замену и какие продукты придётся отозвать при компрометации.
Цель не в том, чтобы объявить любой чужой код подозрительным и попытаться прочитать каждую его строку. Безопасность цепочки поставок — это управление неизбежным доверием: его сокращают, обосновывают, технически ограничивают и делают проверяемым. Сначала статья разбирает граф такого доверия и способы его атаковать, затем — защиту зависимостей и сборки, сведения о происхождении и составе артефакта и практический аудит собственного проекта.
Добавляя прямую зависимость, проект обычно получает и её транзитивные зависимости. Так возникает граф из десятков или сотен компонентов. Транзитивное доверие означает, что решение о прямой зависимости распространяется и на её поддерево. При этом зависимости исполняются неодинаково: одни попадают в рабочий процесс, другие нужны только для тестов или сборки. Риск зависит от того, где код запускается, какие данные и секреты он видит и может ли изменить артефакт.
Особенно важны инструменты сборки и установки. Во многих экосистемах пакет может запустить сценарий ещё во время установки. Поэтому команда «установить и посмотреть» уже может исполнить чужой код с правами разработчика или CI. До установки новый пакет можно изучить по метаданным и архиву, а пробную установку выполнить в изолированной среде без рабочих секретов. Это пример границы доверия из модели угроз.
Атака на популярный компонент, реестр или сборочную платформу даёт большой рычаг: одна успешная компрометация может доставить вредоносное изменение многим потребителям. Код приходит по штатному каналу обновления и поэтому может миновать меры, рассчитанные на внешнее вторжение. Это не делает бесполезными периметр, ревью и контроль доступа, но требует распространить их на сам канал поставки.
Вторая причина — масштаб экосистем и неравномерность их ресурсов. Критичный компонент иногда сопровождает небольшая команда без формального процесса выпуска и резервного сопровождающего. Такой риск есть и у открытого, и у коммерческого ПО: лицензия или репутация сами по себе не защищают учётную запись сопровождающего, репозиторий, CI или реестр. Поэтому оценивают не только сам код, но и права на выпуск, способ сборки, темп обновлений и способность проекта реагировать на инцидент.
Основные способы вмешательства в цепочку стоит знать поимённо — защита в разделах 4–6 адресует каждый из них.
Общий знаменатель у этих приёмов: атакующий использует уже существующий канал доверия. Защищать нужно не только приложение, но и путь от исходников и внешних компонентов до запущенного артефакта.
Первая линия защиты — сократить ненужное доверие и сделать оставшееся видимым. Основные меры:
Инженерный конвейер часто относится к числу самых привилегированных систем организации: его отдельные стадии читают исходники, получают секреты, публикуют артефакты и развёртывают их. Если атакующий изменил процесс сборки, репозиторий приложения может остаться чистым, а вредоносный код появится в результате. Поэтому защищают и разделяют сам конвейер:
Зрелая цепочка позволяет ответить на два вопроса о работающем артефакте. Первый: откуда он взялся? На него отвечает свидетельство о происхождении (provenance): машиночитаемое утверждение связывает дайджест артефакта с исходниками, входами, идентификатором сборщика и процессом сборки. Подпись позволяет проверить целостность и подписавшего утверждение. Но проверяющей стороне всё равно нужна политика доверия: какой сборщик, репозиторий и процесс допустимы для данного выпуска. Действительная подпись неизвестного или неразрешённого субъекта ничего не разрешает.
SLSA описывает наращиваемые гарантии происхождения и целостности сборки. Они помогают отличить выпуск доверенного конвейера от артефакта, собранного в другом месте, но не доказывают, что исходный код безвреден.
Второй вопрос: из чего артефакт состоит? На него отвечает SBOM (Software Bill of Materials) — машиночитаемый перечень компонентов, их версий и связей. Его создают для конкретного артефакта и связывают с ним идентификатором или дайджестом. Полезно также фиксировать контрольные суммы компонентов, контекст генерации и известные пробелы: неполный, устаревший или относящийся к другой сборке перечень создаёт ложную уверенность.
Когда появляется сообщение об уязвимости, SBOM помогает сопоставить его с парком артефактов. Но перечень состава не определяет автоматически, достижим ли уязвимый код и затронута ли конкретная конфигурация; для решения нужны данные об эксплуатации, анализ уязвимости и иногда машиночитаемое заявление VEX о её применимости. Происхождение и SBOM дополняют друг друга: первое описывает путь артефакта, второе — его состав. Это средства прослеживаемости и управляемого реагирования, а не сертификаты безвредности.
Работайте только с проектом, который вам разрешено исследовать, либо с его учебной копией. Не запускайте непроверенные сценарии установки на рабочей машине, не передавайте отчёты с внутренними именами во внешние службы и не помещайте реальные секреты в лабораторное окружение.
Задание 1. Измерить дерево. Получите средствами менеджера список прямых и транзитивных зависимостей. Разделите их хотя бы на рабочие, сборочные и тестовые. Выберите незнакомый компонент, найдите приведшую к нему прямую зависимость и определите, исполняется ли он при сборке или в рабочей среде.
Задание 2. Проверить фиксацию и уязвимости. Убедитесь, что lock-файл содержит контрольные суммы, хранится в репозитории и используется CI в неизменяемом режиме. Запустите штатный аудитор — например, npm audit или pip-audit — и для трёх находок запишите серьёзность, достижимость, доступное исправление и срок решения. Не применяйте автоматическое исправление, пока не просмотрели предлагаемые изменения.
Задание 3. Изучить установку без риска. По метаданным, архиву пакета и документации выясните, какие зависимости запускают сценарии установки. Если выполнение необходимо, используйте одноразовую среду без рабочих секретов и с ограниченной сетью. Зафиксируйте, к каким файлам, переменным и адресам такой сценарий получил бы доступ в обычной среде разработчика.
Задание 4. Составить карту прав сборки. Для каждой стадии конвейера выпишите доступ к исходникам, секретам, сети, реестрам и средам развёртывания. Найдите лишнее право, измените конфигурацию через обычное ревью и проверьте тестовым запуском, что стадия продолжает работать.
Задание 5. Связать SBOM с артефактом. Создайте SBOM при помощи Syft, Trivy или штатного средства сборки в формате SPDX либо CycloneDX. Запишите дайджест исследованного артефакта и проверьте, что перечень относится именно к нему. Выберите компонент и смоделируйте сообщение об уязвимости: по SBOM установите, присутствуют ли нужные компонент и версия, а затем перечислите данные, которых одного перечня не хватает для решения о затронутости.
Задание 6*. Проверить разрешение источников. На бумаге возьмите вымышленное имя внутреннего пакета и проследите, в какой реестр обратится менеджер при разных версиях и при недоступности частного источника. Не публикуйте настоящее внутреннее имя. Запишите правило, которое закрепляет пространство имён за частным реестром и запрещает публичный откат.
Задание 7*. Проверить происхождение. Подпишите учебный артефакт и его свидетельство о происхождении, затем проверьте ожидаемые дайджест, репозиторий и идентификатор сборщика. Измените один байт артефакта и убедитесь, что проверка завершается ошибкой. Объясните, почему проверка любой действительной подписи без политики доверия была бы недостаточна.