Back

Простая защита от атак на цепочку поставок npm

Простая защита от атак на цепочку поставок npm

Каждый раз, когда вы запускаете npm install, вы доверяете коду из сотен пакетов, которые никогда не читали. Именно это доверие и эксплуатируют злоумышленники. В сентябре 2025 года червь Shai-Hulud незаметно собирал учётные данные через вредоносные install-скрипты и массово перепубликовывал скомпрометированные пакеты — всё это до того, как командная строка возвращала управление. Компрометация Axios в марте 2026 года повторила тот же сценарий: вредоносные версии (1.14.1 и 0.30.4) подтягивали зависимость с хуком postinstall, выполнявшим payload удалённого доступа.

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

Ключевые выводы

  • Скрипты жизненного цикла npm (preinstall, install, postinstall, prepare) автоматически выполняются с полными правами пользователя, что делает их путём наименьшего сопротивления для атак на цепочку поставок.
  • Установка ignore-scripts=true в .npmrc блокирует самый распространённый вектор атаки одной строкой конфигурации.
  • Пакеты с нативными бинарниками, такие как sharp, bcrypt и sqlite3, всё равно можно собирать выборочно с помощью npm rebuild после установки без скриптов.
  • CI-проверка package-lock.json на новые записи "hasInstallScript": true помечает зависимости, требующие проверки до слияния.
  • Сочетание ignore-scripts=true с min-release-age=7 добавляет задержку, помогающую избежать свежескомпрометированных версий пакетов.

Почему скрипты жизненного цикла — основной риск

Когда вы устанавливаете npm-пакет, Node автоматически запускает любые скрипты, определённые в его package.json: preinstall, install, postinstall и prepare. Эти хуки выполняются с вашими полными правами пользователя, получая доступ к ~/.ssh, ~/.aws/credentials, токенам в ~/.npmrc и каждой переменной окружения вашей оболочки.

Никакого подтверждения. Никакой песочницы. Просто молчаливое выполнение.

В атаке на Axios вредоносная транзитивная зависимость plain-crypto-js использовала хук postinstall для установки трояна удалённого доступа. В атаке-импероснации Bitwarden CLI хук preinstall загружал крупный обфусцированный payload, который собирал облачные учётные данные и пытался распространиться через npm-доступ публикации жертвы.

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

Основная защита: npm ignore-scripts

Добавьте одну строку в .npmrc вашего проекта:

ignore-scripts=true

Это указывает npm пропускать все скрипты жизненного цикла во время установки. Вредоносный хук postinstall просто не запустится.

Для разовой установки без изменения .npmrc:

npm install --ignore-scripts

Важная оговорка: это не полное решение. Атака на Bitwarden регистрировала вредоносный бинарник через поле bin в package.json — это означает, что payload всё равно выполнялся при вызове пользователем команды bw, даже при активном --ignore-scripts. Ни один флаг не устраняет все риски.

Что ломается и как с этим справиться

Некоторым легитимным пакетам нужны скрипты жизненного цикла для компиляции нативных бинарников. К распространённым относятся sharp, bcrypt, sqlite3, canvas и puppeteer (который скачивает Chromium).

Проверьте, использует ли ваш проект какие-либо из них:

npm ls --all | grep -E "(sharp|bcrypt|sqlite3|canvas|puppeteer|node-gyp)"

Если есть совпадения, после установки пересоберите только эти конкретные пакеты:

npm install --ignore-scripts
npm rebuild sharp --ignore-scripts=false
npm rebuild bcrypt --ignore-scripts=false

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

Обнаружение новых install-скриптов до их выполнения

Вместо того чтобы реагировать постфактум, помечайте новые зависимости со скриптами установки до их слияния. Добавьте эту проверку в ваш CI-пайплайн:

# Detect newly added packages with install scripts in PRs
if git diff origin/main -- package-lock.json | \
     grep -E '^\+\s*"hasInstallScript": true' > /dev/null; then
  echo "⚠️ New install script detected—manual review required"
  exit 1
else
  echo "✅ No new install scripts"
fi

В атаке на Axios plain-crypto-js был совершенно новой зависимостью с хуком postinstall. Эта проверка пометила бы его до слияния PR.

Подробнее о том, как npm записывает install-скрипты в lock-файлы, см. в официальной документации package-lock.json.

Чего эти меры защиты не покрывают

Будем честны насчёт ограничений:

  • Lock-файлы не предотвращают атаки, если разработчик запустил npm install в окне компрометации и отравил lock-файл до того, как вы это заметили.
  • npm audit ловит только известные вредоносные пакеты — новые атаки не появятся в его базе данных.
  • 2FA для npm-аккаунтов не защищает потребителей от пакета, легитимно опубликованного скомпрометированным мейнтейнером.
  • --ignore-scripts не блокирует вредоносный код, встроенный в сам runtime JavaScript пакета.

Помогает многоуровневый подход. Сочетайте ignore-scripts=true с min-release-age=7 в .npmrc (требуется npm v11.10.0 или новее), чтобы избежать установки пакетов, опубликованных за последнюю неделю — окна, в которое большинство атак активны и не обнаружены. Эта настройка задокументирована в документации конфига npm в разделе min-release-age.

С чего начать

Добавьте это в ваш .npmrc сегодня:

ignore-scripts=true
min-release-age=7

Затем добавьте CI-проверку на новые записи hasInstallScript для каждого PR, который затрагивает package-lock.json. Эти два изменения закрывают паттерн атак, использовавшийся в недавних компрометациях цепочки поставок npm — без необходимости в новых инструментах, платных сервисах или существенных изменениях в рабочем процессе.

Заключение

Вы не можете прочитать каждую строку каждого пакета, от которого зависите, но можете резко ограничить то, что этим пакетам разрешено делать во время установки. Отключение скриптов жизненного цикла по умолчанию, пересборка только тех нативных пакетов, которым вы доверяете, и пометка новых записей hasInstallScript в CI вместе нейтрализуют наиболее активно эксплуатируемый вектор атаки в экосистеме npm. Ни одна из этих мер не требует новых поставщиков или бюджета — только нескольких строк в .npmrc и одной CI-проверки. Внедрите их сегодня, и следующая атака в стиле Shai-Hulud окажет на ваш проект гораздо меньшее влияние.

Часто задаваемые вопросы

Это может сломать пакеты, которые компилируют нативные бинарники во время установки, такие как sharp, bcrypt, sqlite3, canvas и puppeteer. После запуска npm install с отключёнными скриптами используйте npm rebuild с именем пакета и флагом --ignore-scripts=false, чтобы скомпилировать только проверенные вами доверенные пакеты. Большинство чисто JavaScript-зависимостей будут работать без изменений.

Нет. Он блокирует самый распространённый вектор — хуки жизненного цикла вроде postinstall — но не остановит вредоносный код в runtime JavaScript пакета или вредоносный бинарник, зарегистрированный через поле bin. Атака-импероснация Bitwarden CLI обошла ignore-scripts именно так. Сочетайте его с min-release-age, проверкой lock-файлов и аудитом зависимостей для многоуровневой защиты.

Настройка min-release-age требует npm версии 11.10.0 или новее. Проверьте свою версию командой npm --version и при необходимости обновите её через npm install -g npm@latest. После настройки убедитесь, что параметр активен, командой npm config get min-release-age.

Закоммитьте файл .npmrc с ignore-scripts=true в репозиторий, чтобы CI автоматически наследовал настройку. Для сборок, которым действительно нужна нативная компиляция, добавьте явные команды npm rebuild (с --ignore-scripts=false) для конкретных требуемых пакетов. Это сохраняет безопасное значение по умолчанию, одновременно документируя, каким именно пакетам разрешено выполнять код установки.

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay