Разрешения
Разрешения могут использоваться для управления доступом процесса Node.js к системным ресурсам или действиями, которые процесс может выполнять с этими ресурсами. Разрешения также могут управлять тем, к каким модулям могут получить доступ другие модули.
- Разрешения на основе модулей управляют тем, какие файлы или URL-адреса доступны другим модулям во время выполнения приложения. Это может использоваться для управления доступом модулей к сторонним зависимостям, например.
Если вы обнаружили потенциальную уязвимость безопасности, обратитесь к нашей Политике безопасности.
Модульные разрешения
Политики
Node.js содержит экспериментальную поддержку создания политик при загрузке кода.
Политики — это функция безопасности, предназначенная для обеспечения гарантий относительно того, какой код Node.js может загружать. Использование политик предполагает безопасные практики для файлов политик, такие как обеспечение того, что файлы политик не могут быть перезаписаны приложением Node.js, используя права доступа к файлам.
Лучшей практикой будет обеспечение того, что манифест политики является только для чтения для работающего приложения Node.js, и что файл не может быть изменен работающим приложением Node.js каким-либо образом. Типичная настройка заключается в создании файла политики с другим идентификатором пользователя, чем тот, под которым работает Node.js, и предоставлении прав чтения пользователю, под которым работает Node.js.
Включение
Флаг --experimental-policy может использоваться для включения функций политик при загрузке модулей.
После этого все модули должны соответствовать файлу манифеста политики, переданному в флаг:
node --experimental-policy=policy.json app.js copy
Манифест политики будет использоваться для принудительного выполнения ограничений на код, загружаемый Node.js.
Для предотвращения подмены файлов политики на диске, можно предоставить целостность самого файла политики через --policy-integrity. Это позволяет выполнять node и проверять содержимое файла политики, даже если файл изменен на диске.
node --experimental-policy=policy.json --policy-integrity="sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0" app.js copy
Функции
Поведение при ошибках
Когда проверка политики завершается с ошибкой, Node.js по умолчанию выбросит ошибку. Можно изменить поведение при ошибках на один из нескольких вариантов, определив поле «onerror» в манифесте политики. Доступны следующие значения для изменения поведения:
-
"exit": немедленно завершит процесс. Никакой код очистки запускаться не будет. -
"log": запишет ошибку в месте сбоя. -
"throw": выбросит JS-ошибку в месте сбоя. Это значение по умолчанию.
{
"onerror": "log",
"resources": {
"./app/checked.js": {
"integrity": "sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0"
}
}
} copy Проверки целостности
Файлы политики должны использовать проверки целостности с строками Subresource Integrity, совместимыми с атрибутом integrity браузера, связанным с абсолютными URL-адресами.
При использовании require() или import все ресурсы, участвующие в загрузке, проверяются на целостность, если указан манифест политики. Если ресурс не соответствует целостности, указанной в манифесте, будет выброшена ошибка.
Пример файла политики, который позволит загрузить файл checked.js:
{
"resources": {
"./app/checked.js": {
"integrity": "sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0"
}
}
} copy Каждый ресурс, указанный в манифесте политики, может быть одного из следующих форматов для определения его расположения:
- Строка относительного URL к ресурсу из манифеста, например
./resource.js,../resource.js, или/resource.js. - Полная строка URL-адреса ресурса, например
file:///resource.js.
При загрузке ресурсов весь URL-адрес должен совпадать, включая параметры запроса и фрагмент хэша. ./a.js?b не будет использоваться при попытке загрузить ./a.js и наоборот.
Для генерации строк целостности можно использовать скрипт, такой как node -e 'process.stdout.write("sha256-");process.stdin.pipe(crypto.createHash("sha256").setEncoding("base64")).pipe(process.stdout)' < FILE.
Целостность может быть указана как булево значение true для принятия любого тела для ресурса, что может быть полезно для разработки локально. Не рекомендуется в производстве, так как это позволит считать допустимыми неожиданные изменения ресурсов.
Перенаправление зависимостей
Приложению может потребоваться отправить исправленные версии модулей или предотвратить доступ всех модулей ко всем остальным модулям. Перенаправление может быть использовано для перехвата попыток загрузить модули, которые нужно заменить.
{
"resources": {
"./app/checked.js": {
"dependencies": {
"fs": true,
"os": "./app/node_modules/alt-os",
"http": { "import": true }
}
}
}
} copy Зависимости индексируются по запрошенной строке спецификатора и имеют значения, которые могут быть либо true, либо null, строкой, указывающей на модуль для разрешения, или объектом условий.
Строка спецификатора не выполняет никакого поиска и должна точно соответствовать предоставленным значениям require() или import, за исключением этапа канонизации. Поэтому, если политика использует несколько разных строк для указания на один и тот же модуль (например, исключая расширение), может потребоваться несколько спецификаторов.
Строки спецификаторов канонизируются, но не разрешаются до использования для сопоставления, чтобы обеспечить некоторую совместимость с картами импорта, например, если ресурсу file:///C:/app/server.js было дано следующее перенаправление из политики, расположенной по адресу file:///C:/app/policy.json:
{
"resources": {
"file:///C:/app/utils.js": {
"dependencies": {
"./utils.js": "./utils-v2.js"
}
}
}
} copy Любой спецификатор, используемый для загрузки file:///C:/app/utils.js, будет перехвачен и перенаправлен на file:///C:/app/utils-v2.js вместо этого, независимо от использования абсолютного или относительного спецификатора. Однако, если используется спецификатор, который не является строкой абсолютного или относительного URL-адреса, он не будет перехвачен. Таким образом, если используется импорт, такой как import('#utils'), он не будет перехвачен.
Если значение перенаправления равно true, будет использован параметр «dependencies» в начале файла политики. Если это поле в начале файла политики равно true, используются стандартные алгоритмы поиска Node для поиска модуля.
Если значение перенаправления — строка, она разрешается относительно манифеста и сразу же используется без поиска.
Любой спецификатор, для которого попытка разрешения завершится неудачей и который не указан в зависимостях, приведет к ошибке в соответствии с политикой.
Перенаправление не препятствует доступу к API через такие средства, как прямой доступ к require.cache или через module.constructor, которые позволяют получить доступ к загрузке модулей. Перенаправление политики затрагивает только спецификаторы для require() и import. Для блокировки пути загрузки модулей через переменные требуются другие средства.
Для модуля может быть указано булево значение true для карты зависимостей, чтобы разрешить загрузку любого спецификатора без перенаправления. Это может быть полезно для локальной разработки и может иметь какое-то обоснованное применение в производстве, но следует использовать с осторожностью после аудита модуля для проверки корректности его поведения.
Аналогично "exports" в package.json, зависимости также могут быть указаны как объекты, содержащие условия, которые определяют способ загрузки зависимостей. В приведенном выше примере "http" разрешен, когда условие "import" является частью его загрузки.
Значение null для разрешенного значения места расположения модуля приводит к сбою разрешения. Это можно использовать для предотвращения некоторых видов динамического доступа.
Неизвестные значения для местоположения разрешенного модуля приводят к ошибкам, но не гарантируется совместимость с будущими версиями.
Пример: исправленная зависимость
Перенаправленные зависимости могут предоставить ослабленные или измененные функции, которые соответствуют применению. Например, записывать данные о времени выполнения функций, оборачивая исходный код:
const original = require('fn');
module.exports = function fn(...args) {
console.time();
try {
return new.target ?
Reflect.construct(original, args) :
Reflect.apply(original, this, args);
} finally {
console.timeEnd();
}
}; copy Области
Используйте поле "scopes" манифеста для установки конфигурации для многих ресурсов одновременно. Поле "scopes" работает путем сопоставления ресурсов по их сегментам. Если область или ресурс содержит "cascade": true, неизвестные спецификаторы будут искаться в содержащей их области. Содержащая область для каскадирования находится путем рекурсивного сокращения URL-адреса ресурса путем удаления сегментов для специальных схем, сохраняя конечные "/" суффиксы и удаляя параметр запроса и фрагмент хэша. Это приводит к окончательному сокращению URL-адреса до его источника. Если URL-адрес не является специальным, область будет расположена по источнику URL. Если область не найдена для источника или в случае непрозрачных источников, может использоваться строка протокола как область. Если область не найдена для протокола URL, будет использована конечная пустая строка "" области.
Обратите внимание, что blob: URL-адреса принимают свой источник из содержащегося в них пути, и поэтому область "blob:https://nodejs.org" не окажет никакого влияния, так как ни один URL не может иметь источник blob:https://nodejs.org; URL-адреса, начинающиеся с blob:https://nodejs.org/ будут использовать https://nodejs.org для своего источника, и таким образом https: для своей области протокола. Для URL-адресов непрозрачных источников blob: у них будет blob: для своей области протокола, поскольку они не принимают источники.
Пример
{
"scopes": {
"file:///C:/app/": {},
"file:": {},
"": {}
}
} copy Для файла, расположенного по адресу file:///C:/app/bin/main.js, будут проверены следующие области в порядке:
"file:///C:/app/bin/"
Это определяет политику для всех файловых ресурсов внутри "file:///C:/app/bin/". Это не в поле "scopes" политики и будет пропущено. Добавление этой области в политику приведет к тому, что она будет использоваться до области "file:///C:/app/".
"file:///C:/app/"
Это определяет политику для всех файловых ресурсов внутри "file:///C:/app/". Это в поле "scopes" политики, и это определит политику для ресурса по адресу file:///C:/app/bin/main.js. Если область имеет "cascade": true, любые невыполненные запросы о ресурсе будут делегированы следующей соответствующей области для file:///C:/app/bin/main.js, "file:".
"file:///C:/"
Это определяет политику для всех файловых ресурсов внутри "file:///C:/". Это не в поле "scopes" политики и будет пропущено. Оно не будет использоваться для file:///C:/app/bin/main.js, если "file:///" не установлено на каскадирование или не находится в поле "scopes" политики.
"file:///"
Это определяет политику для всех файловых ресурсов на localhost. Это не в поле "scopes" политики и будет пропущено. Оно не будет использоваться для file:///C:/app/bin/main.js, если "file:///" не установлено на каскадирование или не находится в поле "scopes" политики.
"file:"
Это определяет политику для всех файловых ресурсов. Оно не будет использоваться для file:///C:/app/bin/main.js, если "file:///" не установлено на каскадирование или не находится в поле "scopes" политики.
""
Это определяет политику для всех ресурсов. Оно не будет использоваться для file:///C:/app/bin/main.js, если "file:" не установлено на каскадирование.
Целостность, использующая области
Установка целостности true в области установит целостность любого ресурса, не найденного в манифесте, на true.
Установка целостности null в области установит целостность любого ресурса, не найденного в манифесте, на отказ в сопоставлении.
Отсутствие атрибута integrity эквивалентно установлению integrity в значение null.
"cascade" для проверок целостности будут игнорироваться, если "integrity" явно задано.
Следующий пример позволяет загружать любой файл:
{
"scopes": {
"file:": {
"integrity": true
}
}
} copy Перенаправление зависимостей с использованием областей
Следующий пример позволит получить доступ к fs для всех ресурсов в рамках ./app/:
{
"resources": {
"./app/checked.js": {
"cascade": true,
"integrity": true
}
},
"scopes": {
"./app/": {
"dependencies": {
"fs": true
}
}
}
} copy Следующий пример позволит получить доступ к fs для всех ресурсов data::
{
"resources": {
"data:text/javascript,import('node:fs');": {
"cascade": true,
"integrity": true
}
},
"scopes": {
"data:": {
"dependencies": {
"fs": true
}
}
}
} copy Пример: эмуляция import maps
Учитывая import map:
{
"imports": {
"react": "./app/node_modules/react/index.js"
},
"scopes": {
"./ssr/": {
"react": "./app/node_modules/server-side-react/index.js"
}
}
} copy {
"dependencies": true,
"scopes": {
"": {
"cascade": true,
"dependencies": {
"react": "./app/node_modules/react/index.js"
}
},
"./ssr/": {
"cascade": true,
"dependencies": {
"react": "./app/node_modules/server-side-react/index.js"
}
}
}
} copy Import maps предполагают, что по умолчанию можно получить любой ресурс. Это означает, что "dependencies" на верхнем уровне политики должно быть установлено в значение true. Политики требуют, чтобы это было явно указано, так как это позволяет кросс-связывать все ресурсы приложения, что не имеет смысла во многих сценариях. Они также предполагают, что любая область имеет доступ ко всем областям, разрешённым в её зависимостях; все области, эмулирующие import maps, должны установить "cascade": true.
Import maps имеют только одну область верхнего уровня для своих «импортов». Таким образом, для эмуляции "imports" используйте область "". Для эмуляции "scopes" используйте область "scopes" аналогично тому, как работает "scopes" в import maps.
Ограничения: Политики не используют совпадение строк для поиска областей. Они выполняют поиск по URL. Это означает, что такие вещи, как blob: и data: URL могут не быть полностью совместимы между двумя системами. Например, import maps могут частично соответствовать data: или blob: URL путём разделения URL по символу /, политики же намеренно этого не поддерживают. Для blob: URL области import maps не принимают источник blob: URL.
Кроме того, import maps работают только с import, поэтому может быть целесообразно добавить условие "import" ко всем отображениям зависимостей.
© 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/dist/latest-v18.x/docs/api/permissions.html