Экосистема npm пережила заметный сдвиг: вредоносные пакеты перестали быть мелким шумом вокруг опечаток в названиях библиотек. Атаки всё чаще бьют по реальным пакетам, учётным записям сопровождающих, сборочным конвейерам и токенам публикации. Главная цель — не компьютер отдельного разработчика, а вся цепочка выпуска программного обеспечения.
Раньше типичный риск выглядел проще: злоумышленник регистрировал пакет с похожим названием, ждал случайной установки и пытался украсть криптокошелёк или переменные окружения. Теперь сценарий стал опаснее. Вредоносный код попадает в легитимный пакет, запускается при установке зависимости, ищет секреты, забирает токены npm и GitHub, а затем использует их для заражения новых пакетов. Исследователи Unit42 называют эти инциденты частью более широкой картины: атакующие всё активнее используют скомпрометированные инструменты разработчиков, GitHub Actions, Docker Hub, VS Code-расширения и npm-пакеты для кражи учётных данных и распространения вредоносного кода..
Почему npm стал удобной целью
npm — крупнейшая часть JavaScript-инфраструктуры. Через него ставят библиотеки для фронтенда, серверных приложений, инструментов сборки, расширений, CLI-утилит и внутренних корпоративных проектов. Разработчик часто не видит весь список кода, который попадает в проект: один пакет тянет десятки зависимостей, те — свои зависимости, и дерево быстро становится непрозрачным.
Опасность усиливается из-за жизненного цикла установки. npm поддерживает служебные скрипты, например preinstall, install и postinstall. Они могут запускаться автоматически при установке пакета. Эта возможность нужна для легитимных задач: собрать нативный модуль, подготовить бинарник, проверить окружение. Тот же механизм удобен для атаки: пользователь вводит привычную команду, а вредоносный код стартует до того, как приложение вообще запустили.
Для корпоративной разработки это особенно критично. У локального разработчика могут быть SSH-ключи, токены GitHub, доступ к облакам, конфиги Kubernetes и ключи пакетных реестров. В CI/CD всё ещё хуже: там часто лежат секреты публикации, параметры деплоя, доступ к контейнерным реестрам, облачным аккаунтам и production-инфраструктуре.
Shai-Hulud показал новую модель атаки
Один из главных переломных эпизодов произошёл 15 сентября 2025 года. В npm появились вредоносные версии популярных пакетов, внутри которых был установочный скрипт для сбора секретов. Украденные данные уходили в публичные GitHub-репозитории, созданные атакующими. Кампания получила имя Shai-Hulud. Её важная особенность — червеподобное распространение: если вредонос находил npm-токены, он мог публиковать заражённые версии пакетов, к которым у жертвы был доступ.
Именно этот механизм сделал атаку опасной для всей экосистемы. Один скомпрометированный сопровождающий мог стать точкой заражения для проектов, которыми он управляет. Один заражённый CI-процесс мог дать доступ к токенам публикации. Один забытый секрет в окружении превращался в способ размножения вредоноса.
Позже исследователи зафиксировали новую волну Shai-Hulud. Через десять недель после первой кампании были обнаружены сотни новых вредоносных пакетов. JFrog оценивал вторую волну как более сложную: объём новых заражённых пакетов был ниже, чем в первой волне, но техника атаки стала продуманнее. В публикации фигурируют 796 новых вредоносных пакетов против более чем 1150 в начальной кампании.
Bitwarden CLI: короткое окно, высокий риск
В апреле 2026 года похожая логика проявилась в инциденте с @bitwarden/cli@2026.4.0. Bitwarden сообщил, что вредоносный пакет был доступен через npm-путь доставки CLI 22 апреля 2026 года с 5:57 PM до 7:30 PM по восточному времени США. Компания указала, что пользователи, которые не устанавливали CLI из npm в этот промежуток, не считались затронутыми. Проверка Bitwarden не нашла признаков доступа к пользовательским хранилищам, продакшн-системам или продакшн-данным.
Полтора часа для npm — достаточно много, если пакет ставится автоматически в сборках, контейнерах, тестовых окружениях или обновляется без ручной проверки. В подобных историях время публикации вредоносной версии измеряется минутами, а последствия — ротацией токенов, аудитом CI/CD и проверкой журналов.
Инцидент также показал слабое место современных поставок ПО: пользователь может доверять бренду и официальному названию пакета, но проблема возникает в механизме публикации. Злоумышленнику не обязательно взламывать само приложение. Достаточно получить путь к выпуску пакета или токен, через который пакет отправляется в реестр.
Mini Shai-Hulud: удар по SAP-разработчикам
Следующая заметная волна получила название Mini Shai-Hulud. Она была зафиксирована 29 апреля 2026 года и затронула четыре npm-пакета, связанные с SAP Cloud Application Programming Model и MTA-инструментами: mbt@1.2.48, @cap-js/sqlite@2.2.2, @cap-js/postgres@2.2.2 и @cap-js/db-service@2.10.1. Эти пакеты используются в корпоративной разработке, а значит часто запускаются рядом с облачными ключами, GitHub-токенами и секретами деплоя. Совокупная недельная загрузка затронутых пакетов оценивалась примерно в 570 тыс. установок.
Схема выглядела так: заражённые версии добавляли preinstall-хук, который скачивал среду выполнения JavaScript Bun с GitHub Releases и запускал через неё замаскированный стилер размером около 11,6 МБ. Phoenix Security также описала попытки закрепления через файлы настроек инструментов разработки: .claude/settings.json и .vscode/tasks.json. Вредонос пытался закрепиться через .claude/settings.json и .vscode/tasks.json, то есть через настройки инструментов, которые могут запускать действия при старте сессии или открытии рабочей папки.
В этой кампании атакующие искали токены GitHub и npm, облачные ключи, Kubernetes-конфиги, переменные окружения, данные GitHub Actions и настройки инструментов для разработки с ИИ-ассистентами. Отдельно описан приём с чтением памяти процесса GitHub Actions Runner.Worker: так вредонос пытался достать секреты, которые обычно маскируются в логах сборки.
Украденные данные складывались в GitHub не открытым текстом. Они шифровались и отправлялись в публичные репозитории с характерным описанием A Mini Shai-Hulud has Appeared. Это создаёт две проблемы. Защитники могут увидеть сам репозиторий, но не прочитать содержимое без ключа атакующих. Жертва может не сразу понять масштаб утечки, потому что данные выглядят как набор зашифрованных файлов.
Что общего у новых npm-атак
Общая схема повторяется всё чаще. Сначала злоумышленники получают возможность опубликовать вредоносную версию: через украденный токен, ошибку в настройке публикации, компрометацию сопровождающего или слабую защиту CI/CD. Затем в пакет добавляется установочный скрипт. При установке он собирает секреты из окружения, файловой системы, облачных метаданных и процессов сборки. После этого вредонос пытается расширить доступ: опубликовать новые заражённые версии, добавить workflow в GitHub Actions, создать репозитории для выгрузки данных или закрепиться в файлах проекта.
Раньше защитники часто искали вредонос по названию пакета. Теперь этого мало. Угроза может прийти через настоящий пакет с настоящим именем и реальной историей загрузок. Подозрительным становится не только новый пакет, но и новая версия старого пакета, неожиданный установочный скрипт, скачивание внешнего бинарника во время установки, появление Bun там, где его не использовали, и изменение файлов CI/CD.
Отдельная проблема — транзитивные зависимости. Команда может не указывать заражённый пакет напрямую. Его подтянет другая библиотека через допустимый диапазон версий. Из-за этого проверка package.json не даёт полной картины. Нужны lock-файлы, журнал установки и точное понимание, какие версии реально попали в сборку.
Почему удаление пакета не решает проблему
После запуска вредоносного кода проект уже нельзя считать чистым только из-за удаления зависимости. Если вредонос успел прочитать токены, они могли быть отправлены атакующим. Если он получил npm-токен, он мог опубликовать новые версии пакетов. Если нашёл GitHub-токен, то мог создать репозитории, поменять workflow, добавить ключи или прочитать приватные данные. Если запуск был в CI/CD, под угрозой оказываются секреты сборки и деплоя.
Корректная реакция начинается с изоляции окружения. Дальше нужно определить, где именно выполнялась заражённая версия: локальная машина, контейнер сборки, self-hosted runner, GitHub Actions, корпоративный Jenkins, GitLab CI или другая система. После этого меняются токены npm, GitHub, облачные ключи, SSH-ключи, секреты CI/CD и учётные данные, которые могли быть доступны процессу установки.
Проверять стоит не только зависимости. Важны новые публичные репозитории в GitHub, неожиданные workflow-файлы, изменения в .npmrc, .github/workflows, .vscode, .claude, shell-конфигах, Dockerfile, lock-файлах и журналах публикации пакетов. Для npm-проектов нужно сверить версии, которые реально устанавливались, а не только текущий package.json.
Как снизить риск
Полностью убрать риск невозможно, но можно сильно сократить окно атаки. Для этого нужно:
-
Фиксировать версии через lock-файлы и использовать воспроизводимую установку, например npm ci. Но npm ci не отключает install-скрипты само по себе. Если нужен запрет жизненных циклов установки, нужен отдельный флаг или настройка ignore-scripts. OWASP рекомендует использовать --ignore-scripts для отключения выполнения скриптов сторонних пакетов при установке. Фиксация версия не спасает от уже заражённой зафиксированной версии, но мешает незаметно подтянуть свежую вредоносную публикацию через широкий диапазон версий.
-
Ограничить установочные скрипты. В части окружений можно включать ignore-scripts=true и разрешать исключения вручную. В корпоративной сборке стоит отделить установку зависимостей от окружения, где лежат production-секреты. Установка пакетов не должна происходить с правами, достаточными для публикации релизов или управления облаком.
-
Ограничить токены. У npm-токенов должны быть короткий срок жизни, минимальные права и многофакторная защита для сопровождающих. Для GitHub лучше использовать короткоживущие токены и OpenID Connect там, где это уместно, но сами настройки доверенной публикации нужно проверять отдельно: слишком широкий OIDC-binding превращается в путь к публикации вредоносной версии.
-
Следить за поведением сборки. В нормальном проекте внезапное скачивание Bun, создание публичного GitHub-репозитория, изменение
.github/workflows, обращение к облачным метаданным во время npm install или чтение памяти процессов CI — сильные сигналы атаки. Это контроль поведения сборочного процесса. -
Вести перечень компонентов. SBOM, внутренний прокси-реестр, кэширование проверенных версий и задержка перед автоматическим обновлением зависимостей помогают выиграть время. Для популярных пакетов несколько часов между публикацией и установкой могут решить многое: вредоносную версию успеют снять, а защитные системы — добавить индикаторы компрометации.