Node Package Manager (npm) предоставляет набор сценариев для разработчиков и сопровождающих пакетов для поддержки событий жизненного цикла пакета. Эти сценарии представляют значительную ценность для разработчиков, позволяя им выполнять различные задачи или настройки в рамках процесса установки пакета. Например, с помощью сценариев postinstall разработчики могут автоматизировать такие задачи, как создание ресурсов, настройка переменных среды, запуск миграций или любые задачи, которые могут выполняться автоматически.

Свойство scripts файла package.json определяет команды, запускаемые жизненным циклом пакета, и зависит от разрабатываемого вами пакета. На сегодняшний день npm поддерживает ограниченное количество сценариев жизненного цикла в любом scripts свойстве package.json файла.

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

Прошлые инциденты безопасности

Произошло несколько громких инцидентов, которые оказали реальное влияние на разработчиков JavaScript, в том числе:

Многие другие инциденты безопасности JavaScript и Node.js курируются в репозитории Awesome Node.js Security.

Безопасность хранимых данных

Специалисты по безопасности определяют защиту активов, когда данные хранятся или находятся в состоянии покоя с помощью Data-at-rest, а не когда они находятся в пути или обрабатываются. Основное внимание уделяется защите конфиденциальной информации, хранящейся в базах данных, файловых системах или постоянном хранилище. Безопасность хранимых данных направлена ​​на предотвращение несанкционированного доступа, раскрытия или подделки данных, пока они неактивны. Для обеспечения безопасности данных в состоянии покоя доступны различные меры, такие как:

  • Расшифровка по запросу: расшифровка только данных, необходимых для выполнения текущей задачи, и сохранение остальных данных в зашифрованном виде для предотвращения несанкционированного доступа.
  • Логика управления доступом: проверка личности отправителя запроса с помощью такого механизма, как пароль, двухфакторная аутентификация или биометрические данные, предоставляемые операционной системой (например, FaceID), что позволяет ограничить раскрытие информации. ресурс для нежелательных людей.

Поверхность атаки разработчика

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

Какие данные лежат в незашифрованном виде на компьютере разработчика?

  • Переменные среды через текстовые файлы, такие как .env (доступны для потребления через пакет dotenv).
  • Файлы конфигурации для проектов, хранящиеся в виде файла JSON, например config.json.
  • Ключи SSH для доступа к Github/Gitlab, которые доступны в папке ~/.ssh.
  • И… горячие клавиши macOS!

Замена текста в macOS

macOS по умолчанию имеет функцию под названием Text Replacements, скрытую внутри приложений системных настроек. Эта функция позволяет пользователям быстро заменить слово другим словом. Совсем недавно я узнал, что разработчик из известной компании использовал замену текста, чтобы заменить ключевое слово @card информацией о своей кредитной карте. Несмотря на то, что номер кредитной карты без даты истечения срока действия или CVV не раскрывает ваши деньги посторонним, он добавляет им возможность для атаки.

Эксфильтрация замены текста на клавиатуре

Сочетания клавиш хранятся в папке defaults, которая соответствует файлу .plist, поддерживаемому файловой системой, где-то в вашей локальной папке. Выполнение следующей команды вернет настроенные вами замены текста, которые также доступны через приложение «Системные настройки».

Помните, что следующий код не требует sudo доступа и может быть выполнен любым процессом на вашем компьютере.

> defaults read -g NSUserDictionaryReplacementItems
(
  {
    on = 1;
    replace = "@ssh-key";
    with = "my-secret-password";
  }
)

Эту же команду можно выполнить через execSync в Node.js и без проблем проанализировать с помощью операции жизненного цикла postinstall, поддерживаемой менеджером пакетов npm.

Ниже приведен пример сценария Node.js, который злоумышленники могут использовать для доступа к замене текста в macOS и кражи конфиденциальных данных:

import { execSync } from 'node:child_process'

const decoder = new TextDecoder()
const res = execSync('defaults read -g NSUserDictionaryReplacementItems')
const text_replacements = decoder.decode(res)

console.log(text_replacements)

Чтобы убедиться, что приведенный выше код работает, когда этот пакет установлен, мы обновим файл манифеста пакета следующим образом package.json:

{
  "name": "my-useful-library",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "postinstall": "node ./retrieve.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

При распространении через npm и загрузке разработчиком эта библиотека будет напрямую выполнять наш пользовательский скрипт для получения и обработки замен клавиатуры. Если вы невнимательны, легко пропустить строку, содержащую > node ./retrieve.js.

➜  vulnerable npm i

> [email protected] postinstall
> node ./retrieve.js

up to date, audited 1 package in 192ms

found 0 vulnerabilities
➜  vulnerable

Защита

Что вы можете сделать как разработчик, чтобы снизить риски безопасности, связанные с вредоносными пакетами npm, и общие проблемы безопасности, связанные с произвольным выполнением команд из пакетов в вашем дереве зависимостей?

Игнорировать скрипты при установке пакета npm

Защититься от пакетов, использующих postinstall скриптов, можно. npm предоставляет конфигурацию --ignore-scripts при установке пакетов.

➜  npm i  --ignore-scripts
up to date, audited 1 package in 124ms
found 0 vulnerabilities

Используйте безопасные значения по умолчанию npm

Менеджер пакетов npm также имеет файл конфигурации с именем .npmrc. Вы можете изменить настройки по умолчанию с помощью npm CLI, чтобы обеспечить безопасные значения по умолчанию:

➜  npm config set ignore-scripts true
➜  npm i
up to date, audited 1 package in 126ms
found 0 vulnerabilities

Безопасное хранилище

Самое главное, вы никогда не должны хранить конфиденциальную информацию в виде простого текста. Если вам необходимо хранить его в виде обычного текста из-за других требований, вы всегда должны делать ресурс доступным через многофакторную аутентификацию.