Безпека ланцюга постачання

Модель загроз та обґрунтування заходів контролю npm-залежностей сайту

Щодо самих заходів контролю та щоденних процедур див. Керування залежностями. Суміжні теми безпеки мають власні розділи: привілеї тригерів робочих процесів і токенів — у CI-робочих процесах, а повідомлення про вразливості — у політиці безпеки.

Модель загроз

Серпневий хробак npm 2026 року (повідомлення про безпеку) визначив поточну позицію: зламані облікові записи підтримувачів публікували шкідливі версії популярних npm-пакунків. Скрипти життєвого циклу пакунків, які виконуються під час встановлення, запускали корисне навантаження та поширювали його за допомогою викрадених облікових даних. До стримування було уражено кілька PR-гілок цього репозиторію; жодна не досягла main або виробничого середовища.

Шляхи атак, важливі для цього репозиторію:

  • Визначення версій: будь-яке встановлення, яке визначає діапазони версій, може завантажити щойно опублікований шкідливий випуск.
  • Скрипти життєвого циклу: виконання скриптів під час встановлення дозволяє шкідливому пакунку скомпрометувати хости учасників, виконавців CI та образи збірки.
  • Встановлення без нагляду: завдання CI та образ збірки Netlify встановлюються без нагляду людини, як і сеанси агентів.
  • Визначення назв: виклик інструмента, який може звернутися до реєстру за назвою (npx), виконує той пакунок, якому належить ця назва, коли локальне встановлення застаріле або відсутнє.

Рішення щодо дизайну

Наскрізна тема — закриватися при збої (fail closed): коли захід контролю неможливо забезпечити, встановлення завершується збоєм, а не продовжується без нього.

Кожне рішення відповідає шляху атаки, упорядковані приблизно за моментом дії. Підсумкова таблиця зіставляє рішення з їхнім забезпеченням.

  • Кожна залежність, пряма чи транзитивна, — це поверхня, якої може досягти нападник.
    • Мінімізація залежностей: невикористані та зручні залежності видаляються, а не зберігаються.
  • Встановлення, яке визначає діапазони версій, може завантажити щойно опублікований шкідливий випуск.
    • Встановлення з блокування: встановлення є точними щодо блокування, відтворюючи зафіксований і перевірений package-lock.json. Єдиний виняток: локальний npm install може перезаписати незгодне блокування; такі перезаписи виявляє перевірка.
    • Явне визначення версій: визначення версій відбувається лише під час навмисних оновлень залежностей, ніколи як побічний ефект встановлення.
    • Визначення версій лише для випусків, що відстоялися: навіть явне визначення версій ігнорує випуски, молодші за період охолодження; відкликання шкідливих випусків на стороні реєстру потребує кількох днів.
  • Скрипти пакунка, що виконуються під час встановлення, запускають код нападника на хостах учасників та машинах збірки: шлях корисного навантаження хробака.
    • Виконуються лише перевірені скрипти життєвого циклу: скрипти життєвого циклу є стандартно забороненими.
      • Схвалення є точними щодо версії, тому скомпрометований патч-випуск не може успадкувати схвалення свого попередника.
      • Перевірки фіксують і заборони, тож відсутність відповіді завжди означає «не перевірено».
      • Винятки називаються та повторно вмикаються безпосередньо в місці використання, ніколи не послаблюючи стандартну позицію.
  • Власне встановлення npm Netlify виконується без нагляду, поза скриптами, які контролює цей репозиторій, і його неможливо вимкнути.
  • Бінарний файл, викликаний за назвою, яку можна визначити через реєстр, виконує те, що претендує на цю назву, коли локальне встановлення застаріле; сквотінг незареєстрованої назви бінарного файлу довів це у червні.
    • Викликайте бінарні файли, а не назви: обвʼязка репозиторію ніколи не використовує голий npx; бінарні файли беруться зі встановленого дерева залежностей або завершуються гучним збоєм.
  • Захід контролю, який мовчки перестає застосовуватися, гірший за його відсутність.
    • Блокування при збої на старому npm: встановлення завершується збоєм, а не продовжується, коли активний npm занадто старий, щоб застосовувати налаштування .npmrc.
    • Перевірка замість довіри: інертність і точність щодо блокування — це перевірені твердження, а не припущення.

Забезпечення з першого погляду:

РішенняЗабезпечується
Мінімізація залежностейРішення підтримувача під час перегляду залежностей; механічного контролю немає
Встановлення з блокуванняnpm ci у кожному контракті встановлення
Явне визначення версійКонвенція, підкріплена блокуванням: неочікуване визначення перезаписує його, що позначає перевірка
Визначення версій лише для випусків, що відстоялисяЗахід охолодження, однаково для npm та Renovate
Виконання лише перевірених скриптів життєвого циклуДозволений список у строгому режимі; неперевірене завершує встановлення збоєм
Нейтралізація автоматичного встановленняЗахід інертного автоматичного встановлення
Викликайте бінарні файли, а не назвиПравило жодного голого npx; дисципліна рецензування, механічного контролю немає
Блокування при збої на старому npmМінімальна версія npm зі строгою перевіркою рушія
Перевірка замість довіриПеревірки чистого робочого дерева завершують збірку збоєм; попередження postinstall при локальних перезаписах блокування

Попередні спроби

  • Стандартна заборона запуску скриптів життєвого циклу — це напрямок екосистеми:
    • pnpm і Yarn стандартно блокують скрипти залежностей.
    • Прийнятий npm RFC #54 впроваджує ту саму модель у npm через allowScripts, включно із записами, точними щодо версій.
  • Періоди охолодження випусків — усталена практика:
  • Набір заходів відповідає усталеним рекомендаціям фреймворків:
Востаннє змінено July 12, 2026: [uk] Ukrainian documentation for OpenTelemetry (4cc17d8b)