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

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

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

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

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

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

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

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

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

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

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

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

РішенняЗабезпечується
Мінімізація залежностейРішення підтримувача під час перегляду залежностей; механічного контролю немає
Встановлення з блокуванняnpm ci у кожному контракті встановлення
Явне визначення версійКонвенція і вимкнений lockFileMaintenance у renovate.json5
Визначення версій лише для випусків, що відстоялисяОхолодження в npm і Renovate
Виконання лише перевірених скриптів життєвого циклуДозволений список у строгому режимі; неперевірене завершує встановлення збоєм
Відмова від Hugo installer overridesОбгортка перезбірки’s environment screen, перед будь-якою спробою перезбірки
Нейтралізація автоматичного встановленняЗахід інертного автоматичного встановлення
Викликайте бінарні файли, а не назвиПравило жодного голого npx; дисципліна рецензування, механічного контролю немає
Блокування при збої на старому npmМінімальна версія npm зі строгою перевіркою рушія
Перевірка замість довіриАудит ланцюга постачання, перевірки чистого робочого дерева, попередження postinstall при перезаписах блокування

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

  • Стандартна заборона запуску скриптів життєвого циклу — це напрямок екосистеми:
    • pnpm і Yarn стандартно блокують скрипти залежностей.
    • Прийнятий npm RFC #54 впроваджує ту саму модель у npm через allowScripts, включно із записами, точними щодо версій.
  • Періоди охолодження випусків — усталена практика:
    • pnpm стандартно відкладає випуски, молодші на день.
    • Renovate’s npm minimumReleaseAge security preset встановлює 3 дні, відстежуючи 72-годинне вікно unpublish npm; довше значення, що використовується тут, в лінії з охолодженнями, прийнятими в інших місцях екосистеми.
  • Набір заходів відповідає усталеним рекомендаціям фреймворків:
Востаннє змінено July 12, 2026: [uk] Ukrainian documentation for OpenTelemetry (408b37c7)