Атаки на цепочку: typosquatting и компрометация пакетов
Иногда проблема не в том, что хорошая библиотека уязвима, а в том, что в проект попала откровенно вредоносная.
Атака на цепочку поставок — это внедрение вредоносного кода не напрямую в вашу систему, а в один из компонентов, которым вы доверяете: пакет, его обновление или реестр, откуда вы его берёте.
Этот урок — концептуальный обзор того, как злоумышленники заражают зависимости, и, главное, как от этого защититься. Никаких рецептов атаки здесь нет: цель — научиться распознавать и предотвращать такие сценарии как защитник.
Зачем это знать защитнику
Установка пакета часто выполняет произвольный код (скрипты postinstall в npm, setup.py в Python) с правами разработчика или CI-раннера. Это значит, что вредоносный пакет получает доступ к вашим переменным окружения, токенам, исходникам — ещё до того, как вы запустите своё приложение. Понимание векторов помогает выстроить защиту на уровне политики, а не надеяться на удачу.
Как устроены такие атаки (на уровне принципа)
Typosquatting
Злоумышленник публикует пакет с именем, похожим на популярный, в расчёте на опечатку или невнимательность: reqeusts вместо requests, crossenv вместо cross-env, djagno вместо django. Разработчик, копируя команду из устаревшего гайда или просто промахнувшись по клавише, ставит подделку. Внутри — рабочая копия настоящей библиотеки плюс «нагрузка», которая, например, выгружает переменные окружения.
Dependency confusion (путаница реестров)
Многие компании используют внутренние пакеты с именами вроде acme-internal-utils, недоступные публично. Если менеджер пакетов настроен искать сначала в публичном реестре, злоумышленник может опубликовать в публичном npm/PyPI пакет с тем же именем и более высокой версией. Установщик решит, что это «более свежая» версия, и подтянет публичную подделку вместо внутренней.
Компрометация легитимного пакета
Самый опасный сценарий: атакуют не имя, а сам популярный пакет. Способы — угон аккаунта мейнтейнера (украденный или подобранный пароль, отсутствие 2FA), социальная инженерия (злоумышленник входит в доверие и получает права на публикацию), внедрение через зависимость самого пакета. Дальше выходит вредоносное обновление уже доверенной библиотеки, и оно автоматически прилетает всем, у кого версия не зафиксирована.
Как это работает под капотом
Ключевая механика — момент установки. Рассмотрим, что бывает в манифесте npm:
{
"scripts": {
"postinstall": "node ./setup.js"
}
}
Скрипт postinstall выполняется автоматически при npm install. Для легитимного пакета он может, например, скомпилировать нативный модуль. Для вредоносного — это точка запуска нагрузки. Именно поэтому простое скачивание зависимости уже опасно: код исполняется до вашего ревью.
Вторая механика — алгоритм разрешения версий. Установщик по умолчанию стремится взять наибольшую подходящую версию из всех доступных источников. Без явного приоритета приватного реестра это и открывает dependency confusion: подделка с версией 99.0.0 «выигрывает» у внутренней 1.2.0.
Как защититься
Защита строится из нескольких слоёв — ни один не идеален в одиночку:
- Проверяйте имя перед установкой. Прежде чем добавить пакет, откройте его страницу: сколько загрузок, как давно публикуется, кто мейнтейнер, есть ли репозиторий. Свежесозданный пакет с именем-омонимом популярного и нулём звёзд — красный флаг. Копируйте имена из официальной документации, а не из случайных статей.
- Пиннинг версий + lock-файл. Фиксированная точная версия с хешем (через lock-файл) означает, что вредоносное обновление не прилетит само. Вы обновитесь только осознанно, прогнав тесты и проверки.
- Скоупы и приватные реестры. Внутренние пакеты публикуйте под организационным скоупом (
@acme/utils) и настройте менеджер так, чтобы скоуп@acmeрезолвился ТОЛЬКО из приватного реестра. Это закрывает dependency confusion.
# .npmrc — приватный скоуп идёт только во внутренний реестр
@acme:registry=https://nexus.internal.acme.com/repository/npm/
//nexus.internal.acme.com/repository/npm/:_authToken=${NPM_TOKEN}
npm ci --ignore-scripts), а нужные нативные сборки разрешайте точечно. Это снижает риск автозапуска нагрузки.Юридическое напоминание: публикация вредоносного пакета — это создание и распространение вредоносного ПО (ст. 273 УК РФ). Анализировать такие техники допустимо только в образовательных целях и на своих стендах; цель этого материала — защита, а не воспроизведение атак.
Итоги
- Вредоносный код попадает в проект через typosquatting (похожие имена), dependency confusion (путаница приватного и публичного реестра) и компрометацию легитимных пакетов.
- Опасность в том, что установка пакета часто выполняет произвольный код (postinstall) ещё до запуска вашего приложения.
- Защита многослойная: проверка имени, пиннинг версий с lock-файлом, приватные реестры и скоупы, отключение install-скриптов в CI, карантин обновлений.
- 2FA на публикацию своих пакетов защищает их пользователей от угона вашего аккаунта.