Capabilities とセキュリティ

このページ

サンドボックス化されたプラグインは、デフォルトで分離されています。自身のKVとstorageの読み書き以上のことを行うには、プラグインはmanifestcapabilityを宣言する必要があります。サンドボックスブリッジは、これらの宣言に基づいて、ホストが提供するすべてのAPIを制御します。content:readを宣言していないプラグインはctx.contentを取得できず、network:requestを宣言していないプラグインはctx.httpを取得できません。

このページでは、各capabilityが何を付与するか、サンドボックスがそれらをどのように強制するか、そして何が強制できないかについて説明します。

Capabilitiesの宣言

Capabilitiesはemdash-plugin.jsoncに記述され、slugやその他の信頼契約と一緒に配置されます:

{
	"slug": "plugin-hello",
	// ...identity + profile...

	"capabilities": ["content:read", "network:request"],
	"allowedHosts": ["api.example.com"]
}

プラグインが実際に必要とするもののみを宣言してください。Capabilityの宣言は、マーケットプレイスがサイト運営者に同意ダイアログで表示する内容でもあります。追加のcapabilitiesは、インストール時の摩擦となり、監査でのセキュリティフラグとなります。

Capabilityリファレンス

Capabilityアクセスを許可
content:readctx.content.get(), ctx.content.list()
content:writectx.content.create(), ctx.content.update(), ctx.content.delete() (content:readを含む)
media:readctx.media.get(), ctx.media.list()
media:writectx.media.getUploadUrl(), ctx.media.upload(), ctx.media.delete() (media:readを含む)
network:requestctx.http.fetch()allowedHostsに制限
network:request:unrestrictedctx.http.fetch() ホスト制限なし(ユーザー設定URLのみ)
users:readctx.users.get(), ctx.users.getByEmail(), ctx.users.list()
email:sendctx.email.send() (設定されたメールプロバイダープラグインが必要)
hooks.email-transport:register排他的なemail:deliverフックの登録を許可(トランスポートプロバイダー)
hooks.email-events:registeremail:beforeSend / email:afterSendフックの登録を許可
hooks.page-fragments:registerpage:fragmentsフックの登録を許可(ネイティブプラグインのみ)

知っておくべきいくつかのこと:

  • 包含関係。 content:writeは自動的にcontent:readを含み、media:writemedia:readを含み、network:request:unrestrictednetwork:requestを含みます。両方をリストする必要はありません。
  • network:request:unrestrictedはユーザー設定URLのために存在します。 運営者が宛先URLを入力するwebhookプラグインは、manifestにないホストに到達する必要があります。常に既知のAPIを呼び出すプラグインは、network:request + allowedHostsを使用すべきです。
  • email:sendは、capabilityだけでなく設定によって制御されます。 プラグインはemail:sendを宣言できますが、ctx.emailは他のプラグインがemail:deliverトランスポートを登録している場合にのみ設定されます。

ネットワークホスト許可リスト

network:requestを持つプラグインは、allowedHostsにリストされているホストのみを取得できます。サブドメインのワイルドカードがサポートされています:

"capabilities": ["network:request"],
"allowedHosts": [
	"api.example.com",     // 正確なホスト
	"*.cdn.example.com"    // cdn.example.comの任意のサブドメイン
]

ブリッジは、リクエストを転送する前に、リクエストURLのホストを許可リストに対してチェックします。宣言されていないホストへのリクエストは、サンドボックスを離れることなくプラグイン内でエラーをスローします。

network:request:unrestrictedは、許可リストのチェックを完全にスキップします。これは、運営者が実行時に宛先URLを設定するプラグイン(webhookセンダー、汎用HTTPフォワーダー)を対象としています。宛先がプラグインの設計の一部であるプラグインでは避けてください。代わりに明示的なホストでnetwork:requestを宣言し、同意ダイアログで運営者にプラグインが正確にどこを呼び出すかを伝えるようにしてください。

サンドボックスが強制すること

サンドボックスランナーがアクティブな場合、ランタイムは以下を強制します:

  1. Capabilityゲート。 PluginContextファクトリーは、対応するcapabilityが宣言されている場合にのみ、ctx.contentctx.mediactx.httpctx.usersctx.emailを設定します。宣言されていないcapabilityのメソッドを呼び出すことはできません。そこにはオブジェクトが存在しません。

  2. StorageとKVのスコープ。 すべてのstorageとKV操作は、プラグインのslugにスコープされます。プラグインは、他のプラグインのKVやstorageコレクションを読み取ることができず、manifestで宣言したstorageコレクションにのみアクセスできます。

  3. ネットワーク分離。 直接のfetch()やその他のネットワークプリミティブは、ランナーによってブロックされます。ネットワークに到達する唯一の方法は、ブリッジのホスト検証を通過するctx.http.fetch()です。

  4. ホストバインディングなし。 サンドボックス化されたプラグインは、環境変数、ファイルシステム、またはプラットフォームバインディングを見ることができません。ホストワーカーがそれらを持っていてもです。プラグインランタイムは、ブリッジと宣言されたcapabilitiesのみを持つクリーンなアイソレートです。

  5. リソース制限。 ランナーは、呼び出しごとにCPU、サブリクエスト、ウォールクロック、メモリの制限を強制できます。正確な制限は、使用しているランナーによって異なります。Cloudflareランナーは、プラットフォームのWorker Loaderの制限(呼び出しあたり50ms CPU、10サブリクエスト、30秒ウォールクロック、約128MBメモリ)を使用します。ランナーの制限を超えるフックは中止されます。EmDashフックタイムアウト(フック設定のtimeout)は、その上により厳しい上限を課します。

サンドボックスが強制しないこと

capabilityシステムがカバーしない、カバーできないいくつかのこと:

  • 付与されたcapability内での動作。 content:writeを持つプラグインは、自分が作成したコンテンツだけでなく、任意のコンテンツを編集できます。Capabilitiesは粗い粒度です。「このプラグインはコンテンツを書くことができる」と言うだけで、「このプラグインは自分が作成したコンテンツのみを書くことができる」とは言いません。監査時のレビューが、プラグインが実際に許可内で何をするかについての唯一のチェックです。
  • Node.jsでの運営者の信頼。 設定されたサンドボックスランナーが利用不可と報告された場合(Cloudflare Worker Loaderなし、Node側のランナーがインストールされていないなど)、sandboxed: []プラグインは起動時にスキップされます。それらをplugins: []に移動してインプロセスで実行することもできますが、その場合、V8アイソレートもリソース制限もなく、プラグインはfetch()を直接呼び出したり環境変数を読み取ったりできます。これをネイティブレベルの信頼として扱ってください。
  • サイドチャネル。 タイミング、ログ出力、保存されたデータは、ホスト環境への適切なアクセス権を持つ人なら誰でも見ることができます。サンドボックスを、それを実行する運営者に対する機密性の境界として使用しないでください。

Capabilityの同意

運営者がマーケットプレイスからサンドボックス化されたプラグインをインストールすると、EmDashは宣言されたcapabilitiesをリストした同意ダイアログを表示します。capabilitiesを追加する更新(たとえば、以前はコンテンツを読むだけだったプラグインが、今はネットワークリクエストを行いたい場合など)は、capability差分として表示され、新しいバージョンが有効になる前に新たな承認が必要です。

これが、「後で使うかもしれない」としても追加のcapabilitiesを宣言することが重要な理由です。それらはすべてのインストールと更新で摩擦として現れ、セキュリティ監査は明らかに必要以上のものを要求するプラグインにフラグを立てます。プラグインが使用するものを正確にリストし、プラグインが実際に使い始めたときに実際のバージョンで新しいcapabilitiesを追加してください。

バンドル時の検証

emdash-plugin bundleemdash-plugin publishは追加のチェックを実行します:

  • 宣言されたすべてのcapabilityは、認識されたセットに含まれている必要があります(タイプミスはビルドを失敗させます)。
  • network:requestは空でないallowedHostsを必要とします。network:request:unrestrictedは、それが空であることを必要とします。manifestリファレンスを参照してください。
  • バンドルされたbackend.jsは、Node.jsビルトイン(fspathchild_processなど)をインポートできません。サンドボックスランタイムはそれらを提供しません。

チェックの完全なリストについては、Bundling and publishingを参照してください。