パーミッション#

パーミッションは、Node.jsプロセスがアクセスできるシステムリソース、またはプロセスがそれらのリソースに対して実行できるアクションを制御するために使用できます。パーミッションは、他のモジュールがアクセスできるモジュールも制御できます。

  • モジュールベースのパーミッションは、アプリケーションの実行中に他のモジュールがアクセスできるファイルまたはURLを制御します。これは、たとえば、サードパーティの依存関係がアクセスできるモジュールを制御するために使用できます。

  • プロセスベースのパーミッションは、Node.jsプロセスのリソースへのアクセスを制御します。リソースは完全に許可または拒否されるか、またはそれに関連するアクションを制御できます。たとえば、ファイルシステムの読み取りを許可し、書き込みを拒否できます。

潜在的なセキュリティの脆弱性を見つけた場合は、セキュリティポリシーを参照してください。

モジュールベースのパーミッション#

ポリシー#

安定性: 1 - 実験的

Node.jsには、コードの読み込み時にポリシーを作成するための実験的なサポートが含まれています。

ポリシーは、読み込まれたコードの整合性を確保することを目的としたセキュリティ機能です。

コードの起源を追跡するための起源メカニズムとしては機能しませんが、悪意のあるコードの実行に対する強力な防御策として機能します。コードが読み込まれた後に機能を制限する可能性のあるランタイムベースのモデルとは異なり、Node.jsポリシーは、悪意のあるコードがアプリケーションに完全に読み込まれるのを最初から防ぐことに焦点を当てています。

ポリシーの使用は、ファイルのパーミッションを使用して、ポリシーファイルがNode.jsアプリケーションによって上書きされないようにするなど、ポリシーファイルの安全なプラクティスを前提としています。

ベストプラクティスは、ポリシーマニフェストを実行中のNode.jsアプリケーションに対して読み取り専用にし、実行中のNode.jsアプリケーションによってファイルが変更されないようにすることです。一般的な設定では、Node.jsを実行しているユーザーIDとは異なるユーザーIDとしてポリシーファイルを作成し、Node.jsを実行しているユーザーIDに読み取りパーミッションを付与します。

有効化#

--experimental-policyフラグを使用すると、モジュールの読み込み時にポリシーの機能を有効にできます。

これが設定されると、すべてのモジュールはフラグに渡されたポリシーマニフェストファイルに準拠する必要があります

node --experimental-policy=policy.json app.js 

ポリシーマニフェストは、Node.jsによってロードされたコードに制約を適用するために使用されます。

ディスク上のポリシーファイルの改ざんを防ぐため、ポリシーファイル自体の整合性を--policy-integrityを介して提供できます。これにより、ファイルがディスク上で変更された場合でも、nodeを実行し、ポリシーファイルの内容をアサートできます。

node --experimental-policy=policy.json --policy-integrity="sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0" app.js 
機能#
エラー動作#

ポリシーチェックが失敗した場合、Node.jsはデフォルトでエラーをスローします。ポリシーマニフェストに「onerror」フィールドを定義することにより、エラー動作をいくつかの可能性のいずれかに変更できます。動作を変更するために、次の値を使用できます

  • "exit": プロセスをすぐに終了します.クリーンアップコードは実行できません.
  • "log": 障害の発生箇所にエラーを記録します.
  • "throw": 障害の発生箇所でJSエラーをスローします.これはデフォルトです.
{
  "onerror": "log",
  "resources": {
    "./app/checked.js": {
      "integrity": "sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0"
    }
  }
} 
整合性チェック#

ポリシーファイルは、絶対URLに関連付けられたブラウザのintegrity属性と互換性のあるサブリソース整合性文字列を使用して整合性チェックを使用する必要があります。

require()またはimportを使用する場合、ポリシーマニフェストが指定されている場合、読み込みに関与するすべてのリソースの整合性がチェックされます。リソースがマニフェストにリストされている整合性と一致しない場合、エラーがスローされます。

ファイルchecked.jsの読み込みを許可するポリシーファイルの例

{
  "resources": {
    "./app/checked.js": {
      "integrity": "sha384-SggXRQHwCG8g+DktYYzxkXRIkTiEYWBHqev0xnpCxYlqMBufKZHAHQM3/boDaI/0"
    }
  }
} 

ポリシーマニフェストにリストされている各リソースは、その場所を決定するために、次のいずれかの形式にすることができます.

  1. マニフェストからのリソースへの相対URL文字列。例: ./resource.js../resource.js、または/resource.js.
  2. リソースへの完全な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 }
      }
    }
  }
} 

依存関係は、要求された指定子文字列によってキーが付けられ、値はtruenull、解決されるモジュールを指す文字列、または条件オブジェクトのいずれかです.

指定子文字列は検索を実行せず、正規化ステップを除いて、require()またはimportに提供されるものと正確に一致する必要があります。したがって、同じモジュールを指すために複数の異なる文字列を使用する場合(拡張子を除外する場合など)、ポリシーに複数の指定子が必要になる場合があります.

指定子文字列は正規化されますが、たとえば、リソースfile:///C:/app/utils.jsfile:///C:/app/policy.jsonにあるポリシーから次のリダイレクトを受け取った場合など、インポートマップとの互換性を持たせるために、解決される前に使用されます.

{
  "resources": {
    "file:///C:/app/utils.js": {
      "dependencies": {
        "./utils.js": "./utils-v2.js"
      }
    }
  }
} 

file:///C:/app/utils.jsを読み込むために使用される指定子は、絶対指定子または相対指定子を使用するかに関係なく、インターセプトされ、代わりにfile:///C:/app/utils-v2.jsにリダイレクトされます.ただし、絶対URL文字列でも相対URL文字列でもない指定子が使用されている場合、インターセプトされません.したがって、import('#utils')などのインポートが使用された場合、インターセプトされません.

リダイレクトの値がtrueの場合、ポリシーファイルの上部にある「dependencies」フィールドが使用されます.ポリシーファイルの上部にあるそのフィールドがtrueの場合、デフォルトのノード検索アルゴリズムを使用してモジュールが検索されます.

リダイレクトの値が文字列の場合、マニフェストを基準にして解決され、検索せずにすぐに使用されます.

解決が試みられ、依存関係にリストされていない指定子文字列は、ポリシーに従ってエラーになります.

依存関係マップのブール値trueを指定して、モジュールがリダイレクトなしで任意の指定子を読み込むことができるようにすることができます.これは、ローカル開発に役立ち、本番環境で有効な使用方法がある場合がありますが、モジュールを監査して動作が有効であることを確認した後にのみ注意して使用する必要があります.

package.json"exports"と同様に、依存関係の読み込み方法を分岐する条件を含むオブジェクトとして指定することもできます.前の例では、"import"条件が読み込みの一部である場合、"http"が許可されます.

解決された値の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();
  }
}; 
スコープ#

マニフェストの"scopes"フィールドを使用して、多くのリソースの設定を一度に設定します."scopes"フィールドは、セグメントによってリソースを一致させることによって機能します.スコープまたはリソースに"cascade": trueが含まれている場合、不明な指定子は、それらを含むスコープで検索されます.カスケードの包含スコープは、特別なスキームのセグメントを削除し、末尾の"/"接尾辞を保持し、クエリとハッシュフラグメントを削除することにより、リソースURLを再帰的に削減することによって見つかります.これにより、URLが最終的にそのオリジンに縮小されます.URLが特別でない場合、スコープはURLのオリジンによって特定されます.不透明なオリジンの場合、またはオリジンのスコープが見つからない場合、プロトコル文字列をスコープとして使用できます.URLのプロトコルのスコープが見つからない場合、最終的な空の文字列""スコープが使用されます.

注意: blob: URL は、含まれるパスからオリジンを採用するため、"blob:https://node.dokyumento.jp" のスコープは効果がありません。blob:https://node.dokyumento.jp をオリジンとする URL は存在しないためです。blob:https://node.dokyumento.jp/ で始まる URL は、オリジンとして https://node.dokyumento.jp を使用し、したがってプロトコルスコープとして https: を使用します。不透明なオリジンの blob: URL の場合、オリジンを採用しないため、プロトコルスコープは blob: になります。

#
{
  "scopes": {
    "file:///C:/app/": {},
    "file:": {},
    "": {}
  }
} 

file:///C:/app/bin/main.js にあるファイルを指定した場合、以下のスコープが順番にチェックされます。

  1. "file:///C:/app/bin/"

これは、"file:///C:/app/bin/" 内のすべてのファイルベースのリソースに対するポリシーを決定します。これはポリシーの "scopes" フィールドにはなく、スキップされます。このスコープをポリシーに追加すると、"file:///C:/app/" スコープの前に使用されるようになります。

  1. "file:///C:/app/"

これは、"file:///C:/app/" 内のすべてのファイルベースのリソースに対するポリシーを決定します。これはポリシーの "scopes" フィールドにあり、file:///C:/app/bin/main.js にあるリソースのポリシーを決定します。スコープに "cascade": true が設定されている場合、リソースに関する未解決のクエリは、file:///C:/app/bin/main.js の次の関連スコープである "file:" に委任されます。

  1. "file:///C:/"

これは、"file:///C:/" 内のすべてのファイルベースのリソースに対するポリシーを決定します。これはポリシーの "scopes" フィールドにはなく、スキップされます。"file:///C:/app/" がカスケードに設定されているか、ポリシーの "scopes" にない場合を除き、file:///C:/app/bin/main.js には使用されません。

  1. "file:///""

これは、localhost 上のすべてのファイルベースのリソースに対するポリシーを決定します。これはポリシーの "scopes" フィールドにはなく、スキップされます。"file:///C:/" がカスケードに設定されているか、ポリシーの "scopes" にない場合を除き、file:///C:/app/bin/main.js には使用されません。

  1. "file:"

これは、すべてのファイルベースのリソースに対するポリシーを決定します。"file:///"" がカスケードに設定されているか、ポリシーの "scopes" にない場合を除き、file:///C:/app/bin/main.js には使用されません。

  1. ""

これは、すべてのリソースに対するポリシーを決定します。"file:" がカスケードに設定されていない限り、file:///C:/app/bin/main.js には使用されません。

スコープを使用した整合性#

スコープの整合性を true に設定すると、マニフェストにないすべてのリソースの整合性が true に設定されます。

スコープの整合性を null に設定すると、マニフェストにないすべてのリソースの整合性が一致に失敗します。

整合性を指定しないことは、整合性を null に設定することと同じです。

"integrity" が明示的に設定されている場合、整合性チェックの "cascade" は無視されます。

次の例では、任意のファイルの読み込みが許可されます。

{
  "scopes": {
    "file:": {
      "integrity": true
    }
  }
} 
スコープを使用した依存関係のリダイレクト#

次の例では、./app/ 内のすべてのリソースに対して fs へのアクセスが許可されます。

{
  "resources": {
    "./app/checked.js": {
      "cascade": true,
      "integrity": true
    }
  },
  "scopes": {
    "./app/": {
      "dependencies": {
        "fs": true
      }
    }
  }
} 

次の例では、すべての data: リソースに対して fs へのアクセスが許可されます。

{
  "resources": {
    "data:text/javascript,import('node:fs');": {
      "cascade": true,
      "integrity": true
    }
  },
  "scopes": {
    "data:": {
      "dependencies": {
        "fs": true
      }
    }
  }
} 
例: インポートマップのエミュレーション#

インポートマップを指定した場合

{
  "imports": {
    "react": "./app/node_modules/react/index.js"
  },
  "scopes": {
    "./ssr/": {
      "react": "./app/node_modules/server-side-react/index.js"
    }
  }
} 
{
  "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"
      }
    }
  }
} 

インポートマップ は、デフォルトですべてのリソースを取得できると想定しています。これは、ポリシーのトップレベルにある "dependencies"true に設定する必要があることを意味します。アプリケーションのすべてのリソースの相互リンクを有効にするため、多くのシナリオでは意味がないため、ポリシーではオプトインが必要です。また、特定のスコープは、許可された依存関係の上位にあるすべてのスコープにアクセスできると想定しています。インポートマップをエミュレートするすべてのスコープは、"cascade": true を設定する必要があります。

インポートマップには、「インポート」のトップレベルスコープが 1 つだけあります。したがって、「インポート」をエミュレートするには、"" スコープを使用します。「スコープ」をエミュレートするには、インポートマップでの "scopes" の動作と同様に "scopes" を使用します。

注意事項: ポリシーは、スコープのさまざまな検索に文字列マッチングを使用しません。URLトラバーサルを行います。これは、blob:data: URL などは、2 つのシステム間で完全に相互運用できない可能性があることを意味します。たとえば、インポートマップは、/ 文字で URL を分割することにより、data: または blob: URL を部分的に一致させることができますが、ポリシーは意図的に一致させることができません。blob: URL の場合、インポートマップスコープは blob: URL のオリジンを採用しません。

さらに、インポートマップは import でのみ機能するため、すべての依存関係マッピングに "import" 条件を追加することが望ましい場合があります。

保証#
  • ポリシーは、require()import()、または new Module() を使用してモジュールがロードされるときのファイルの整合性を保証します。
  • リダイレクションは、ロードされたモジュールへのアクセスを許可する require.cache への直接アクセスなどの手段による API へのアクセスを妨げません。ポリシーのリダイレクションは、require() および import への指定子にのみ影響します。
  • ポリシーの脅威モデルにおけるモジュール整合性の承認は、ロードされたモジュールがセキュリティ機能を改ざんしたり、回避することさえ許可されていることを意味するため、環境/ランタイムの強化が期待されます。

プロセスベースのパーミッション#

パーミッションモデル#

安定性: 1.1 - 活発な開発

Node.js パーミッションモデルは、実行中に特定のリソースへのアクセスを制限するためのメカニズムです。API はフラグ --experimental-permission の背後に存在し、有効にすると、利用可能なすべてのパーミッションへのアクセスが制限されます。

利用可能なパーミッションは、--experimental-permission フラグによって dokumentiert されます。

Node.js を --experimental-permission で起動すると、fs モジュールを介したファイルシステムへのアクセス、プロセスの生成、node:worker_threads の使用、ネイティブアドオン、およびランタイムインスペクターの有効化が制限されます。

$ node --experimental-permission index.js
node:internal/modules/cjs/loader:171
  const result = internalModuleStat(filename);
                 ^

Error: Access to this API has been restricted
    at stat (node:internal/modules/cjs/loader:171:18)
    at Module._findPath (node:internal/modules/cjs/loader:627:16)
    at resolveMainPath (node:internal/modules/run_main:19:25)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:24)
    at node:internal/main/run_main_module:23:47 {
  code: 'ERR_ACCESS_DENIED',
  permission: 'FileSystemRead',
  resource: '/home/user/index.js'
} 

プロセスの生成とワーカースレッドの作成へのアクセスを許可するには、それぞれ --allow-child-process--allow-worker を使用します。

パーミッションモデルを使用しているときにネイティブアドオンを許可するには、--allow-addons フラグを使用します。

ランタイム API#

--experimental-permission フラグを介してパーミッションモデルを有効にすると、新しいプロパティ permissionprocess オブジェクトに追加されます。このプロパティには、1 つの関数が含まれています。

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 
ファイルシステムパーミッション#

ファイルシステムへのアクセスを許可するには、--allow-fs-read および --allow-fs-write フラグを使用します。

$ node --experimental-permission --allow-fs-read=* --allow-fs-write=* index.js
Hello world!
(node:19836) ExperimentalWarning: Permission is an experimental feature
(Use `node --trace-warnings ...` to show where the warning was created) 

両方のフラグの有効な引数は次のとおりです。

  • * - それぞれすべての FileSystemRead または FileSystemWrite 操作を許可します。
  • コンマ (,) で区切られたパス。それぞれ一致する FileSystemRead または FileSystemWrite 操作のみを許可します。

  • --allow-fs-read=* - すべての FileSystemRead 操作を許可します。
  • --allow-fs-write=* - すべての FileSystemWrite 操作を許可します。
  • --allow-fs-write=/tmp/ - /tmp/ フォルダーへの FileSystemWrite アクセスを許可します。
  • --allow-fs-read=/tmp/ --allow-fs-read=/home/.gitignore - /tmp/ フォルダー **と** /home/.gitignore パスへの FileSystemRead アクセスを許可します。

ワイルドカードもサポートされています。

  • --allow-fs-read=/home/test* は、ワイルドカードに一致するすべてへの読み取りアクセスを許可します。例: /home/test/file1 または /home/test2

ワイルドカード文字 (*) を渡した後、後続のすべての文字は無視されます。たとえば、/home/*.js/home/* と同様に機能します。

パーミッションモデルの制約#

このシステムを使用する前に知っておく必要のある制約があります。

  • モデルは、子ノードプロセスまたはワーカースレッドに継承されません。
  • パーミッションモデルを使用すると、次の機能が制限されます。
    • ネイティブモジュール
    • 子プロセス
    • ワーカースレッド
    • インスペクタープロトコル
    • ファイルシステムアクセス
  • パーミッションモデルは、Node.js 環境がセットアップされた後に初期化されます。ただし、--env-file--openssl-config などの一部のフラグは、環境の初期化前にファイルを読み取るように設計されています。その結果、そのようなフラグはパーミッションモデルのルールに従いません。
  • パーミッションモデルが有効になっている場合、OpenSSL エンジンをランタイムにリクエストすることはできず、組み込みの crypto、https、および tls モジュールに影響します。
制限事項と既知の問題#
  • パーミッションモデルが有効になっている場合、Node.js は無効になっている場合とは異なる方法で一部のパスを解決する場合があります。
  • 相対パスは、CLI (--allow-fs-*) ではサポートされていません。
  • シンボリックリンクは、アクセスが許可されているパスのセット外の場所にも続きます。相対シンボリックリンクは、任意のファイルとディレクトリへのアクセスを許可する可能性があります。パーミッションモデルを有効にしてアプリケーションを起動する場合は、アクセスが許可されているパスに相対シンボリックリンクが含まれていないことを確認する必要があります。