Разрешения
Разрешения можно использовать для управления доступом процесса Node.js к системным ресурсам и действиями, которые процесс может выполнять с этими ресурсами.
- Разрешения на уровне процесса управляют доступом процесса Node.js к ресурсам. Доступ к ресурсу можно полностью разрешить или запретить либо управлять связанными с ним действиями. Например, можно разрешить чтение файловой системы, запретив запись. Эта возможность не защищает от вредоносного кода. Согласно Политике безопасности Node.js, Node.js доверяет любому коду, выполнение которого от него запрашивают.
Модель разрешений реализует подход «ремня безопасности», предотвращая непреднамеренное изменение файлов доверенным кодом или использование ресурсов, доступ к которым не был явно предоставлен. Она не гарантирует безопасность при наличии вредоносного кода. Вредоносный код может обойти модель разрешений и выполнить произвольный код без ограничений, налагаемых этой моделью.
Если вы обнаружили потенциальную уязвимость безопасности, ознакомьтесь с нашей Политикой безопасности.
Разрешения на уровне процесса
Модель разрешений
Стабильность: 2 — Стабильный
Модель разрешений Node.js — это механизм ограничения доступа к определённым ресурсам во время выполнения. API доступен за флагом --permission; при его включении доступ ко всем доступным разрешениям будет ограничен.
Доступные разрешения описаны флагом --permission.
При запуске Node.js с --permission будут ограничены доступ к файловой системе через модуль fs, доступ к сети, создание дочерних процессов, использование node:worker_threads, нативных дополнений, WASI, FFI и включение инспектора среды выполнения (прослушиватель SIGUSR1 создан не будет).
$ node --permission index.js
Error: Access to this API has been restricted
at node:internal/main/run_main_module:23:47 {
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: '/home/user/index.js'
}
consolecopy
Разрешить создание процессов и потоков Worker можно с помощью флагов --allow-child-process и --allow-worker соответственно.
Чтобы разрешить доступ к сети, используйте --allow-net, а чтобы разрешить нативные дополнения при использовании модели разрешений — флаг --allow-addons. Для WASI используйте флаг --allow-wasi. Для FFI используйте флаг --allow-ffi. Модулю node:ffi также требуется флаг --experimental-ffi; он доступен только в сборках с поддержкой FFI.
API среды выполнения
При включении модели разрешений с помощью флага --permission к объекту process добавляется новое свойство permission. Это свойство содержит следующие функции:
permission.has(scope[, reference])
Вызов API для проверки разрешений во время выполнения (permission.has())
process.permission.has('fs.write'); // true
process.permission.has('fs.write', '/home/rafaelgss/protected-folder'); // true
process.permission.has('fs.read'); // true
process.permission.has('fs.read', '/home/rafaelgss/protected-folder'); // false
jscopy
permission.drop(scope[, reference])
Вызов API для отзыва разрешений во время выполнения. Это действие необратимо.
Если вызвать функцию без ссылки, разрешение отзывается для всей области действия. Если передать ссылку, будет отозвано только разрешение для указанного ресурса. Отзыв разрешения влияет только на последующие проверки доступа. Он не закрывает и не отзывает доступ к уже открытым ресурсам, таким как файловые дескрипторы, сетевые сокеты, дочерние процессы или потоки Worker. Приложения должны закрывать или завершать работу этих ресурсов, когда они больше не нужны.
Можно отозвать разрешение только для точно того ресурса, доступ к которому был явно предоставлен. Ссылка, переданная в drop(), должна совпадать с исходным разрешением. Если разрешение было предоставлено с помощью подстановочного символа (*), отозвать можно только разрешение для всей области действия (вызвав drop() без ссылки). Если был предоставлен доступ к каталогу (например, --allow-fs-read=/my/folder), нельзя отзывать разрешения для отдельных файлов внутри него — необходимо отозвать разрешение для того же каталога, к которому изначально был предоставлен доступ.
const fs = require('node:fs');
// Read config at startup while we still have permission
const config = fs.readFileSync('/etc/myapp/config.json', 'utf8');
// Drop read access to /etc/myapp after initialization
process.permission.drop('fs.read', '/etc/myapp');
// This will now throw ERR_ACCESS_DENIED
process.permission.has('fs.read', '/etc/myapp/config.json'); // false
// Drop child process permission entirely
process.permission.drop('child');
jscopyРазрешения для файловой системы
По умолчанию модель разрешений ограничивает доступ к файловой системе через модуль node:fs. Она не гарантирует, что пользователи не смогут получить доступ к файловой системе другими способами, например через модуль node:sqlite.
Чтобы разрешить доступ к файловой системе, используйте флаги --allow-fs-read и --allow-fs-write:
$ node --permission --allow-fs-read=* --allow-fs-write=* index.js Hello world! consolecopy
По умолчанию точки входа вашего приложения включены в список разрешённых путей для чтения файловой системы. Например:
$ node --permission index.js consolecopy
-
index.jsбудет включён в список разрешённых путей для чтения файловой системы
$ node -r /path/to/custom-require.js --permission index.js. consolecopy
-
/path/to/custom-require.jsбудет включён в список разрешённых путей для чтения файловой системы. -
index.jsбудет включён в список разрешённых путей для чтения файловой системы.
Для обоих флагов допустимы следующие аргументы:
-
*— чтобы разрешить все операцииFileSystemReadилиFileSystemWriteсоответственно. - Относительные пути от текущего рабочего каталога.
- Абсолютные пути.
Пример:
-
--allow-fs-read=*— разрешит все операцииFileSystemRead. -
--allow-fs-write=*— разрешит все операцииFileSystemWrite. -
--allow-fs-write=/tmp/— разрешит доступFileSystemWriteк каталогу/tmp/. -
--allow-fs-read=/tmp/ --allow-fs-read=/home/.gitignore— разрешит доступFileSystemReadк каталогу/tmp/и пути/home/.gitignore.
Подстановочные символы также поддерживаются:
-
--allow-fs-read=/home/test*разрешит чтение всего, что соответствует подстановочному символу. Например:/home/test/file1или/home/test2
После символа подстановки (*) все последующие символы будут игнорироваться. Например, /home/*.js будет работать аналогично /home/*.
При инициализации модели разрешений она автоматически добавит подстановочный символ (*) к указанному существующему каталогу. Например, если /home/test/files существует, он будет трактоваться как /home/test/files/*. Однако если каталог не существует, подстановочный символ добавлен не будет, и доступ будет ограничен /home/test/files. Если вы хотите разрешить доступ к каталогу, который ещё не существует, обязательно укажите подстановочный символ явно: /my-path/folder-do-not-exist/*.
Поддержка файла конфигурации
Помимо передачи флагов разрешений в командной строке, их можно указать в файле конфигурации Node.js при использовании экспериментального флага [--experimental-config-file][]. Параметры разрешений необходимо поместить в объект верхнего уровня permission.
Пример node.config.json:
{
"permission": {
"allow-fs-read": ["./foo"],
"allow-fs-write": ["./bar"],
"allow-child-process": true,
"allow-worker": true,
"allow-net": true,
"allow-addons": false,
"allow-ffi": false
}
}
jsoncopy
Если в файле конфигурации присутствует пространство имён permission, Node.js автоматически включает флаг --permission. Запустите с помощью команды:
$ node --experimental-default-config-file app.js consolecopy
Использование модели разрешений с npx
Если вы используете npx для выполнения скрипта Node.js, модель разрешений можно включить, передав флаг --node-options. Например:
npx --node-options="--permission" package-name bashcopy
Эта команда задаёт переменную окружения NODE_OPTIONS для всех процессов Node.js, запущенных npx, не затрагивая сам процесс npx.
Ошибка FileSystemRead с npx
Приведённая выше команда, скорее всего, вызовет ошибку недопустимого доступа FileSystemRead, поскольку Node.js требуется доступ на чтение файловой системы, чтобы найти и выполнить пакет. Чтобы этого избежать:
-
Использование глобально установленного пакета Предоставьте доступ на чтение к глобальному каталогу
node_modules, выполнив команду:npx --node-options="--permission --allow-fs-read=$(npm prefix -g)" package-name bashcopy
-
Использование кэша
npxЕсли вы временно устанавливаете пакет или используете кэшnpx, предоставьте доступ на чтение к каталогу кэша npm:npx --node-options="--permission --allow-fs-read=$(npm config get cache)" package-name bashcopy
Любые аргументы, которые вы обычно передаёте в node (например, флаги --allow-*), также можно передать через флаг --node-options. Благодаря этому при использовании npx легко настраивать необходимые разрешения.
Ограничения модели разрешений
Перед использованием этой системы необходимо учитывать следующие ограничения:
- Модель не наследуется потоком Worker.
- При использовании модели разрешений будут ограничены следующие возможности:
- Нативные модули
- Сеть
- Дочерние процессы
- Потоки Worker
- Протокол инспектора
- Доступ к файловой системе
- WASI
- FFI
- Модель разрешений инициализируется после настройки среды Node.js. Однако некоторые флаги, такие как
--env-fileили--openssl-config, предназначены для чтения файлов до инициализации среды. Поэтому на такие флаги правила модели разрешений не распространяются. То же относится к флагам V8, которые можно установить во время выполнения с помощьюv8.setFlagsFromString. - При включённой модели разрешений нельзя запрашивать движки OpenSSL во время выполнения, что влияет на встроенные модули crypto, https и tls.
- При включённой модели разрешений нельзя загружать расширения, загружаемые во время выполнения, что влияет на модуль sqlite.
- Использование существующих файловых дескрипторов через модуль
node:fsобходит модель разрешений.
process._debugProcess() и активация инспектора в другом процессе
Область действия разрешения kInspector не позволяет текущему процессу открывать собственный инспектор V8. Однако process._debugProcess(pid) — который отправляет внешнему процессу сигнал на уровне ОС (SIGUSR1 в POSIX, удалённый поток в Windows), — не ограничивается областью действия kInspector или какой-либо другой областью действия модели разрешений.
Изолированный процесс, запущенный с --permission без дополнительных разрешений, может вызвать process._debugProcess(pid), чтобы принудительно открыть инспектор V8 в другом процессе Node.js. Для этого целевой процесс не обязан запускаться с --permission — сигнал можно отправить любому процессу Node.js на том же узле от имени того же пользователя ОС.
Это соответствует модели угроз Node.js: Node.js доверяет среде ОС, в которой он работает. Отправка сигналов между процессами — это возможность на уровне операционной системы; за её ограничение отвечает оператор (например, с помощью изоляции процессов на уровне ОС, отдельных пользователей ОС для каждого процесса или профилей seccomp/AppArmor в Linux).
Разработчикам, использующим --permission для изоляции недоверенного кода, следует учитывать, что:
- process._debugProcess() можно вызвать из любого изолированного процесса без дополнительных разрешений.
- Если целевой процесс Node.js работает на том же узле от имени того же пользователя ОС, его можно принудительно заставить открыть инспектор с помощью этого API.
- Чтобы предотвратить это, запускайте изолированные и целевые процессы от имени разных пользователей ОС или используйте механизмы изоляции на уровне ОС за пределами Node.js.
Ограничения и известные проблемы
- Символические ссылки будут прослеживаться даже до местоположений за пределами путей, для которых был предоставлен доступ. Относительные символические ссылки могут открыть доступ к произвольным файлам и каталогам. При запуске приложений с включённой моделью разрешений необходимо убедиться, что в путях, для которых предоставлен доступ, нет относительных символических ссылок.
© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/api/permissions.html