Как избежать ловушки зависимостей [npm]

Пыль после инцидентов с UA-parser-js, coa и rc улеглась, и это прекрасная возможность уделить немного времени и посмотреть, чему мы можем научиться из этого. С некоторыми небольшими изменениями в том, как вы просматриваете зависимости и работаете с ними, вы можете вернуть контроль — вместо того, чтобы позволить зависимостям контролировать вас и подавлять вас.

Проблема безопасности UA-parser выдвигает на первый план две важные вещи для экосистемы npm:

  • Дерево зависимостей сопряжено с риском для безопасности. Ваши прямые зависимости могут быть не вредоносными, но ваши зависимости прямых зависимостей могут быть целевыми. Эти транзитивные зависимости часто исчисляются сотнями и являются большими слабыми местами.
  • Организациям необходимо расширить область безопасности и защитить не только CI/CD. Среды разработки часто бывают более многочисленными и более сложными для контроля, что делает их более вероятной целью для компрометации вредоносными пакетами.

В наше время программ-вымогателей как никогда важно защитить всю вашу организацию — контролировать, какие пакеты разрешены в вашей среде.

Кратко об инциденте с UA-parser-js JavaScript-библиотека ua-parser-js вызвала активную деятельность по обеспечению безопасности, поскольку пакет был взломан, а три вредоносные версии были опубликованы на публичный реестр npm. Еще раз подчеркивая необходимость уделять больше внимания безопасности в экосистемах JavaScript (и других).

Библиотека, используемая для обнаружения браузерных и пользовательских данных, еженедельно загружается разработчиками по всему миру почти 8 миллионами раз в неделю и используется в качестве зависимости для более чем 1200 других пакетов в общедоступном реестре npm.

Дополнительные сведения см. в рекомендациях по безопасности.

Обновление: Вредоносные версии пакетов coa и rc опубликованы 2021-11-04. Та же вредоносная программа и шаблон атаки (и указывающий на того же угонщика), нацеленные на популярные библиотеки поддержки. Вредоносные версии обоих пакетов позже были удалены npm.

Дерево зависимостей и уровни зависимостей

Джо, младший разработчик, помогает своей команде с тестированием приложений в течение выходных. Его окружение чистое, поэтому он старательно устанавливает необходимые зависимости для приложения с помощью npm install.

Приложение имеет только несколько прямых зависимостей, но со всеми транзитивными зависимостями он добавляет более 700 зависимостей. К несчастью для Джо и его компании, уже слишком поздно, поскольку он по незнанию установил троян в свою среду…

Установить зависимости npm с помощью менеджеров пакетов легко — и мы не должны обманывать себя и думать, что все осведомлены и осведомлены о потенциальных проблемах. Все может пойти не так с количеством зависимостей и с пакетами, которым разрешено выполнять произвольные сценарии как часть процесса установки.

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

Взлом, даже не подозревая об этом

В качестве примера возьмем атаку ua-parser-js. Во время инцидента любой пользователь, устанавливающий пакет с разрешением зависимостей, таким как ua-parser-js: ^0.7.xx, получил бы вредоносную версию (0.7.29). Разрешение зависимостей говорит нам получить последнюю версию патча 0.7, если это не исправлено такими факторами, как заблокированные версии зависимостей.

Так кого же это затронуло? Проекты, напрямую зависящие от ua-parser-js, явно оказались под угрозой. Но в типичном случае скомпрометированная зависимость была добавлена ​​как транзитивная зависимость (зависимость от зависимости).
Это привело к тому, что пользователи, работающие с популярными библиотеками и фреймворками, такими как react и angular, сообщили о включении скомпрометированной библиотеки.

С транзитивными зависимостями количество пакетов, от которых зависят ваши проекты, резко возрастает. До такой степени, что быстро становится невозможно полностью понять, что и сколько зависимостей использует ваша команда.

# Example of dependency tree with ua-parser-js included as a transitive dependency. 
# 'npm ls' is used to identify the path for a specific dependency
$ npm ls ua-parser-js
    [email protected]
      [email protected]
       └─┬ [email protected]
         └── [email protected]
# Excerpt from [email protected] where ua-parser-js is included as a dependency
    "dependencies": {
        ...
        "ua-parser-js": "^0.7.18"
        },
# ua-parser-js: ^0.7.18 would resolve to the latest 0.7.x version. Installing a compromised version with malware during the time of the incident.

После ua-parser-js самая большая проблема заключалась не в том, чтобы выяснить, использовали ли ваши приложения ua-parser, а в том, чтобы выяснить, подвергались ли вы воздействию — в любой среде, любым способом, среди сотен разработчиков. Задача, над которой активно работали многие компании, поскольку им не хватало надлежащего контроля над пакетами, которые попадают в их среду.

Как избежать ловушки? Контролировать вместо того, чтобы быть под контролем

Предотвращение подобных проблем в будущем должно быть приоритетом, и любые инвестиции в надлежащую защиту сэкономят время и деньги в долгосрочной перспективе.

Итак, вопрос на миллион долларов — Как нам избежать этого в будущем?
Мы можем смягчить большинство проблем, добавив контроль над зависимостями и процесс управления исправлениями.

  • Избегайте непреднамеренных изменений версий зависимостей
  • Используйте единый источник достоверной информации о зависимостях

Блокировка версий зависимостей

Вы могли бы подумать, снова обсуждение использования lock-файлов? Разве не все должны знать и использовать их сейчас? И я согласен, каждый должен их использовать — но это не так.

Версии зависимостей следует обновлять намеренно, а не как побочный эффект. Последовательные npm install дают слегка отличающиеся и недетерминированные результаты ни в средах CI/CD, ни в средах разработки.

Организации должны внедрить процесс, который обновляет, фиксирует и проверяет файлы блокировки проекта и гарантирует, что каждая последующая установка (и пользователь) использует эти файлы.

Использование диапазонов зависимостей вместо закрепления точных версий зависимостей обеспечивает гибкость экосистемы, но сопряжено с неотъемлемыми рисками безопасности. Использование файлов блокировки (package-lock и yarn.lock) вместе с npm ci для полных и детерминированных установок создает необходимые трения, которые делают обновление версий зависимостей контролируемым процессом.

Единый источник — межсетевой экран зависимостей

Непосредственная зависимость от общедоступных реестров и бесчисленных репозиториев GitHub вместо использования одного источника пакетов быстро делает контроль над потоком зависимостей невыполнимой задачей.

Как вы собираетесь убедиться, что зависимости соответствуют вашей бизнес-политике, что они безопасны и содержат утвержденные лицензии, имея несколько разных источников?

Решение: единый центр, такой как Bytesafe, для обеспечения соблюдения правил и мониторинга потока зависимостей — для каждого разработчика, тестировщика и системы сборки.

Чтобы убедиться, что все используют один и тот же источник реестра и предполагаемые версии, проекты должны включать файл конфигурации .npmrc и файлы package-lock.json или yarn.lock, которые определяют, какой реестр использовать.

# Example .npmrc config setting the default registry to be used by npm clients
registry=https://workspace.bytesafe.dev/r/example-registry/

Не допускайте нежелательных зависимостей в свою организацию. Настройте брандмауэр для ваших зависимостей с помощью Bytesafe!

Спасибо за чтение!