2026 г.

Цепочка поставок ПО: доверие к чужому коду

Проект ЦИТадель

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

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

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

1. Транзитивное доверие

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

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

2. Почему этот вектор привлекателен для атакующих

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

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

3. Каталог приёмов

Основные способы вмешательства в цепочку стоит знать поимённо — защита в разделах 4–6 адресует каждый из них.

  • Захват пакета или канала выпуска. Атакующий крадёт учётные данные сопровождающего, получает права на публикацию или компрометирует сам реестр. Вредоносная версия выходит под знакомым именем и может попасть в автоматическое обновление. Смена сопровождающих сама по себе не атака, но требует повторной оценки доверия.
  • Опечаточный сквоттинг (typosquatting). В публичном реестре появляется пакет, имя которого похоже на имя популярного. Расчёт делается на опечатку, неверную автоподстановку или поверхностное ревью.
  • Путаница зависимостей (dependency confusion). Атакующий публикует в общедоступном реестре пакет с именем внутреннего компонента. Если источник пакета не закреплён, а правила разрешения выбирают публичную версию — например, из-за более высокого номера, — сборка получает чужой код.
  • Компрометация исходников или сборки. Вредоносное изменение попадает в репозиторий через украденную учётную запись или внедряется в компилятор, плагин, действие CI или сборочную среду. Во втором случае исходники приложения могут остаться неизменными, а закладка появится в артефакте.
  • Подмена базового образа или другого входа. Контейнер, SDK или инструмент загружается по изменяемому тегу или из недоверенного источника. Фиксация по дайджесту защищает от незаметной замены позже, но не делает изначально выбранный образ безопасным.

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

4. Гигиена зависимостей

Первая линия защиты — сократить ненужное доверие и сделать оставшееся видимым. Основные меры:

  • Меньше зависимостей. До подключения компонента выясняют, какую задачу он решает, что входит в его транзитивное дерево, где этот код будет исполняться и насколько проект сопровождается. Небольшую общецелевую функцию иногда разумнее реализовать локально. Но криптографию, разбор сложных форматов и протокольную логику не стоит переписывать только ради уменьшения числа пакетов: цена собственной ошибки здесь выше.
  • Проверенный источник. Сверяют точное имя и пространство имён, владельца пакета, историю выпусков, число людей с правом публикации и недавние изменения владельцев. Для критичных компонентов полезно заранее знать, куда сообщать об уязвимости и как быстро проект обычно выпускает исправления.
  • Зафиксированное разрешение зависимостей. Lock-файл с точными версиями и контрольными суммами хранят в репозитории, а CI устанавливает зависимости в режиме, запрещающем незаметно переписать его. Это делает входы повторяемыми и обнаруживает подмену содержимого. Однако точная версия всё равно может оказаться уязвимой или изначально вредоносной.
  • Управляемые обновления и аудит. Робот может находить уязвимости и предлагать обновления, но решение учитывает серьёзность, достижимость уязвимого кода, доступность системы и компенсирующие меры. Для исправлений задают сроки и оформляют временные исключения; изменения проходят тесты и поэтапное развёртывание. Обычный выпуск можно ненадолго выдержать на испытательном контуре, а критическое исправление не следует задерживать по формальному правилу карантина.
  • Контролируемый реестр. Прокси или зеркало дают кэш, журнал и единую политику допуска. Чтобы они защищали от путаницы зависимостей, внутренние пространства имён явно привязывают к частному источнику и запрещают откат к публичному реестру. Одного факта наличия прокси для этого недостаточно.

5. Сборка как объект атаки и защиты

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

  • Наименьшие права стадий. Компиляции не нужен доступ в рабочую среду, тестам — ключ публикации, а заданию из внешнего pull request — секреты проекта. Каждая стадия получает только необходимые ей права, сетевой доступ и данные.
  • Короткоживущие секреты по требованию. Секрет выдаётся из хранилища конкретному заданию на ограниченное время и не попадает в образ, репозиторий или журнал. Где возможно, постоянный ключ заменяют удостоверением рабочей нагрузки и временным токеном.
  • Неизменяемые ссылки на инструменты. Сторонние действия, плагины, компиляторы и базовые образы по возможности фиксируют по коммиту или дайджесту. Если доступен только номер выпуска, дополнительно проверяют источник и целостность. Обновляет ссылку отдельное просматриваемое изменение, а не изменяемый тег вроде latest.
  • Чистая и по возможности герметичная сборка. Одноразовое окружение устраняет состояние от предыдущих запусков. Фиксированный набор входов и запрет произвольных сетевых загрузок дополнительно делают сборку герметичной. Это сужает возможности подмешивания, но само по себе ещё не доказывает происхождение результата.
  • Проверка воспроизводимости. Если независимые сборки из одинаковых входов дают побитно одинаковый результат, подмену среды легче обнаружить. Расхождение требует расследования; совпадение подтверждает повторяемость, но не отсутствие вредоносной логики в самих исходниках.

6. Происхождение и состав: подписи и SBOM

Зрелая цепочка позволяет ответить на два вопроса о работающем артефакте. Первый: откуда он взялся? На него отвечает свидетельство о происхождении (provenance): машиночитаемое утверждение связывает дайджест артефакта с исходниками, входами, идентификатором сборщика и процессом сборки. Подпись позволяет проверить целостность и подписавшего утверждение. Но проверяющей стороне всё равно нужна политика доверия: какой сборщик, репозиторий и процесс допустимы для данного выпуска. Действительная подпись неизвестного или неразрешённого субъекта ничего не разрешает.

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

Второй вопрос: из чего артефакт состоит? На него отвечает SBOM (Software Bill of Materials) — машиночитаемый перечень компонентов, их версий и связей. Его создают для конкретного артефакта и связывают с ним идентификатором или дайджестом. Полезно также фиксировать контрольные суммы компонентов, контекст генерации и известные пробелы: неполный, устаревший или относящийся к другой сборке перечень создаёт ложную уверенность.

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

7. Практикум: аудит собственной цепочки

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

Задание 1. Измерить дерево. Получите средствами менеджера список прямых и транзитивных зависимостей. Разделите их хотя бы на рабочие, сборочные и тестовые. Выберите незнакомый компонент, найдите приведшую к нему прямую зависимость и определите, исполняется ли он при сборке или в рабочей среде.

Задание 2. Проверить фиксацию и уязвимости. Убедитесь, что lock-файл содержит контрольные суммы, хранится в репозитории и используется CI в неизменяемом режиме. Запустите штатный аудитор — например, npm audit или pip-audit — и для трёх находок запишите серьёзность, достижимость, доступное исправление и срок решения. Не применяйте автоматическое исправление, пока не просмотрели предлагаемые изменения.

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

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

Задание 5. Связать SBOM с артефактом. Создайте SBOM при помощи Syft, Trivy или штатного средства сборки в формате SPDX либо CycloneDX. Запишите дайджест исследованного артефакта и проверьте, что перечень относится именно к нему. Выберите компонент и смоделируйте сообщение об уязвимости: по SBOM установите, присутствуют ли нужные компонент и версия, а затем перечислите данные, которых одного перечня не хватает для решения о затронутости.

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

Задание 7*. Проверить происхождение. Подпишите учебный артефакт и его свидетельство о происхождении, затем проверьте ожидаемые дайджест, репозиторий и идентификатор сборщика. Измените один байт артефакта и убедитесь, что проверка завершается ошибкой. Объясните, почему проверка любой действительной подписи без политики доверия была бы недостаточна.

Итоги

  • В цепочку поставок входят не только библиотеки, но и инструменты, действия CI, базовые образы, реестры и среда выпуска. Они образуют граф доверия с разными правами и последствиями компрометации.
  • Атакующий может захватить пакет или канал выпуска, воспользоваться похожим именем, путаницей источников либо вмешаться в сборку и её входы.
  • Lock-файл, контрольные суммы и фиксированные источники делают разрешение зависимостей проверяемым, но не гарантируют безопасность выбранного компонента.
  • Обновления требуют автоматического обнаружения проблем, оценки риска, тестов и поэтапного выпуска. Прокси-реестр защищает от путаницы только при явных правилах пространств имён и источников.
  • Конвейер разделяют по правам, секреты выдают кратковременно, инструменты фиксируют по неизменяемым идентификаторам, а сборки изолируют и по возможности делают герметичными и воспроизводимыми.
  • Подписанное происхождение отвечает «каким доверенным процессом получен этот артефакт», SBOM — «из каких компонентов он состоит». Оба средства улучшают обнаружение и реагирование, но не доказывают безвредность кода.

Литература

  1. NIST SP 800-218, Secure Software Development Framework (SSDF) Version 1.1 — практики защиты разработки, сборки, выпуска и реагирования.
  2. SLSA Specification 1.2 — модель угроз и уровни гарантий происхождения сборки.
  3. CISA, 2025 Minimum Elements for a Software Bill of Materials — минимальные данные и контекст SBOM.
  4. Спецификации форматов SBOM: SPDX и CycloneDX.
  5. OWASP, Software Component Verification Standard; OpenSSF, Scorecard — критерии проверки компонентов и автоматизированные сигналы о практиках проекта.
  6. Sigstore, проверка подписей и удостоверений при помощи Cosign.
  7. Связанные статьи проекта: «Модель угроз», «Инженерный конвейер», «Контейнеры», «Современный JavaScript и TypeScript» и «Наблюдаемость».
404 Not Found

404 Not Found


nginx/1.24.0 (Ubuntu)

Связь с редакцией