Сервис развивается: тестируем формат, собираем идеи, улучшаем сервис. Есть идеи?

Написать
Войти
Дайджесты
Иллюстрация к статье о безопасности npm-пакетов

Безопасный выпуск npm-пакетов и защита от атак на цепочку поставок

Эшелонированная защита npm-пакетов от атак на цепочку поставок в 2026 году: почему статические токены аутентификации в CI/CD создают постоянный риск, как правильно настроить механизм Trusted Publishing через OIDC, зафиксировать GitHub Actions по SHA-хешу и внедрить временную выдержку зависимостей.

Безопасный выпуск npm-пакетов и защита от атак на цепочку поставок

Экосистема JavaScript остается одной из самых привлекательных целей для атак на цепочку поставок (supply chain attacks). Когда разработчики устанавливают популярную библиотеку, они доверяют не только автору кода, но и всей инфраструктуре сборки и доставки пакета. Традиционная модель, в которой релиз выполняется с помощью долгоживущего API-токена, сохраненного в настройках CI/CD-системы, создаёт постоянный риск: утечка секрета из логов, скомпрометированный сторонний шаг сборки или уязвимость в окружении автоматизации открывают злоумышленникам прямой доступ к публикации вредоносных версий под видом легитимного обновления.

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

Анатомия современных атак на экосистему npm

Главная уязвимость классического процесса публикации заключается в длительном сроке жизни токенов аутентификации. Если разработчик генерирует токен доступа в реестре npm и сохраняет его в Secrets репозитория GitHub, этот секрет остаётся валидным до момента его ручной отмены. При компрометации одной из сторонних зависимостей, используемых внутри системы непрерывной интеграции (CI), вредоносный код может прочитать переменные окружения и передать токен на внешний сервер.

После получения доступа злоумышленники внедряют в пакет вредоносные скрипты установки (postinstall), собирающие приватные ключи и токены доступа на компьютерах инженеров и серверах приложений. Поскольку обновления библиотек часто подтягиваются автоматически через диапазон допустимых версий в package.json, вредоносный релиз мгновенно попадает к пользователям до того, как мейнтейнеры успеют обнаружить проблему.

Переход на Trusted Publishing через OIDC

Наиболее эффективным решением проблемы долгоживущих секретов стал переход на механизм доверенной публикации (Trusted Publishing), основанный на открытом стандарте OpenID Connect (OIDC). Вместо постоянного API-токена сервер публикаций npm устанавливает доверительные отношения непосредственно с провайдером CI/CD, таким как GitHub Actions.

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

Для включения данного режима в конфигурации workflow GitHub Actions указывается минимально необходимый набор разрешений:

permissions:
  contents: read
  id-token: write

Право id-token: write позволяет среде получить OIDC-токен, а contents: read обеспечивает доступ к исходному коду только в режиме чтения. Для публичного пакета из публичного репозитория использование Trusted Publishing автоматически генерирует провенанс (Provenance attestation) — цифровое доказательство того, что архив создан именно из указанного коммита в официальном репозитории.

Разделение этапов сборки, тестирования и публикации

Распространенной ошибкой при настройке автоматизации является выполнение тестов, сборки и публикации внутри одного привилегированного шага. Если скрипт тестирования устанавливает все зависимости проекта (включая devDependencies) и запускает сторонние утилиты, любые вредоносные операции в их скриптах установки получают доступ к правам публикации.

Безопасная архитектура пайплайна требует разделения процесса на три независимых этапа (jobs):

  1. Тестирование (test): запуск проверок с правами contents: read. Зависимости устанавливаются с флагом --ignore-scripts, чтобы заблокировать выполнение произвольных команд из сторонних пакетов.
  2. Сборка (build): компиляция исходного кода в артефакты (директорию dist/). Готовые файлы упаковываются и сохраняются как временный артефакт CI (Artifact Handoff). Кэширование сторонних менеджеров пакетов отключается.
  3. Публикация (publish): изолированный шаг, который не выполняет установку зависимостей. Он скачивает ранее сохраненный артефакт dist/, запрашивает OIDC-токен и выполняет команду npm publish --ignore-scripts (или npm stage publish для отправки на двухфакторную проверку).

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

Защита релизных тегов и фиксация Actions по SHA

Даже при использовании OIDC автоматизация запускается на основе событий в репозитории, таких как создание Git-тега версионирования (например, v1.2.0). Если любой разработчик с правом записи в репозиторий может создать тег, он получает возможность инициировать релиз.

Для защиты от несанкционированного запуска публикаций в настройках репозитория настраиваются правила защиты тегов (GitHub Rulesets). Создание тегов формата v* ограничивается исключительно администраторами проекта.

Дополнительным вектором атаки являются сторонние действия (GitHub Actions), подключаемые в workflow. Запись вида uses: actions/checkout@v4 ссылается на плавающий Git-тег. Если учетная запись автора этого action будет взломана, злоумышленники могут передвинуть тег v4 на коммит с вредоносным кодом.

Для предотвращения подобных подмен все сторонние действия должны фиксироваться по полному 40-символьному хэшу SHA коммита с текстовым комментарием:

uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

Фиксация по SHA гарантирует неизменяемость используемого кода автоматизации. При необходимости обновления версии разработчики проводят аудит изменений и обновляют значение хэша.

Временная выдержка релизов и защита от быстрой компрометации

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

Чтобы исключить мгновенное подтягивание свежеопубликованных и еще не проверенных сообществом версий, в современных менеджерах пакетов применяется настройка минимального возраста релиза (Release Age Gate). Данная опция предписывает менеджеру пакетов игнорировать версии, опубликованные менее заданного количества дней назад.

Примеры настройки задержки обновления зависимостей:

  • В npm: npm config set --location=project min-release-age 3
  • В pnpm: параметр minimumReleaseAge: 4320 в .npmrc
  • В Yarn: параметр yarn npmMinimalAgeGate 3d в .yarnrc.yml
  • В Bun: параметр minimumReleaseAge = 259200 в bunfig.toml

Установка трехдневной выдержки создает временной буфер: если в новой версии зависимости содержится вредоносный код, он будет выявлен экспертами по безопасности до того, как пакет попадет в рабочий проект.

Пошаговый чек-лист аудит-проверки релизного пайплайна

Для перевода проекта на безопасную модель публикации мейнтейнерам рекомендуется выполнить следующую последовательность действий:

  1. Настройка Trusted Publisher в npm: В панели управления пакетом на npmjs.com в разделе Settings → Trusted Publisher добавить провайдера GitHub, указать репозиторий и путь к файлу workflow (.github/workflows/publish.yml).
  2. Конфигурация workflow в GitHub Actions: Разделить процессы тестирования, сборки и отправки. В job публикации выставить permissions: { contents: read, id-token: write }, включить скачивание артефакта и исключить npm install.
  3. Фиксация сторонних экшенов: Заменить версионные теги сторонних uses: на полные хэши SHA коммитов.
  4. Защита тегов и отключение старых токенов: В настройках GitHub создать Ruleset, запрещающий не-администраторам создавать теги v*. После успешной проверки OIDC-публикации в настройках npm выбрать пункт Require two-factor authentication and disallow tokens и отменить старые токены.
  5. Проверка инвариантов: Убедиться, что попытка публикации из другого workflow или форка отклоняется политикой npm. Задокументировать регламент обновления SHA экшенов и экстренного отката версий.

Внедрение эшелонированной защиты исключает человеческий фактор и делает процесс выпуска пакетов прозрачным, воспроизводимым и устойчивым к современным угрозам.