mcpField
N
n8n:protect-endpoints

n8n:protect-endpoints

github.com/n8n-ioセキュリティ209.0k244 回インストール今日更新
リポジトリ

n8n:protect-endpoints について

n8nのRESTコントローラー開発向けスキルです。認証済みルートにRBACのアクセススコープを適用し、新しいスコープに必要な設定とテストを説明します。

主な機能

  1. 01プロジェクトまたはグローバルなスコープ設定
  2. 02権限スコープの追加手順
  3. 03未認証ルートの扱い
  4. 04ルートメタデータの回帰テスト
  5. 05サービス層でのデータ絞り込み

活用シーン

  1. 01RESTルートへのアクセス制御追加
  2. 02n8nへの権限スコープ導入
  3. 03公開Webhookの認証確認
  4. 04プロジェクト間のデータ漏えい防止

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/n8n-io/n8n --skill n8n:protect-endpoints

インストール後、Claude Code で/n8n:protect-endpointsを入力して読み込まれたか確認します。

詳細

RBAC で REST エンドポイントを保護する

ルール: @RestController の認証済みルートにはすべて、アクセススコープデコレーターを付ける必要があります。デコレーターなしでルートを追加した場合、IDOR/権限回避の責任は追加した人にあります。

判断基準

URL has :projectId  → @ProjectScope('<resource>:<op>')
URL has no project  → @GlobalScope('<resource>:<op>')
skipAuth: true      → no decorator + comment explaining alternate auth

@ProjectScope は、ユーザーがスコープをグローバルに、または URL で指定されたプロジェクト内で持っていれば成功します。@GlobalScope はプロジェクトとの関係を一切考慮しません。

どちらのデコレーターも @n8n/decorators から提供されます。ミドルウェアは packages/cli/src/controller.registry.ts(createScopedMiddleware)にあり、packages/cli/src/permissions.ee/check-access.ts の userHasScopes を使ってアクセスを解決します。

デコレーターを適用する

import { Get, Post, ProjectScope, RestController } from '@n8n/decorators';

@RestController('/projects/:projectId/widgets')
export class WidgetsController {
  @Post('/')
  @ProjectScope('widget:create')          // create
  async create(...) { ... }

  @Get('/:widgetId')
  @ProjectScope('widget:read')            // read one
  async get(...) { ... }

  @Get('/')
  @ProjectScope('widget:list')            // list
  async list(...) { ... }

  @Patch('/:widgetId')
  @ProjectScope('widget:update')          // update
  async update(...) { ... }

  @Delete('/:widgetId')
  @ProjectScope('widget:delete')          // delete
  async delete(...) { ... }
}

規約:

  • ルートごとにデコレーターを 1 つ使い、HTTP メソッドデコレーターの直下に配置します。
  • 条件に合う最も具体的なスコープを使います。リソースごとに個別の制御が必要でない限り、publish/unpublish/build などの状態変更操作には *:update を再利用します(先例として workflow:publish を参照してください)。
  • :projectId がなく、グローバル限定の操作でもないルートは、通常、設計上の問題を示しています。見つけたら指摘してください。

スコープがまだ存在しない場合

packages/@n8n/permissions/ にリソースと操作を追加します。

  1. src/constants.ee.ts — RESOURCES に追加します(アルファベット順)。
    widget: [...DEFAULT_OPERATIONS, 'execute'] as const,
    
    Scope 共用体(<resource>:<op> のテンプレートリテラル型)は自動的に導出されます。
  2. src/scope-information.ts — 各スコープの表示名と説明を追加します。
  3. src/roles/scopes/project-scopes.ee.ts — プロジェクトロールに追加します。製品側から別途指示がない限り、workflow の先例に合わせます。
    • REGULAR_PROJECT_ADMIN_SCOPES、PERSONAL_PROJECT_OWNER_SCOPES、PROJECT_EDITOR_SCOPES → CRUDL と実行の全スコープ。
    • PROJECT_VIEWER_SCOPES → 読み取り/一覧表示/実行のみ。
    • PROJECT_CHAT_USER_SCOPES → 実行のみ(該当する場合)。
  4. src/roles/scopes/global-scopes.ee.ts — GLOBAL_OWNER_SCOPES に追加します(admin は concat() 経由で継承します)。メンバー/チャットユーザーのグローバルスコープには追加しないでください。これらのロールはプロジェクトとの関係を通じてスコープを取得します。
  5. パーソナルスペースでの公開: <resource>:publish スコープを追加する場合は、パーソナルオーナーの制御を workflow:publish と一致させるため、constants.ee.ts の PERSONAL_SPACE_PUBLISHING_SETTING.scopes にも追加します。
  6. src/roles/custom-role-scopes.ee.ts — 権限マトリックスに表示する操作とともに、リソースを PROJECT_CUSTOM_ROLE_OPERATIONS に表示順で追加します。エディターの SCOPES/SCOPE_TYPES と保存時の許可リスト PROJECT_CUSTOM_ROLE_SCOPES は、いずれもここから導出されます。ここにリソースがないと UI に表示されず、スコープがないと保存時に拒否されます。
  7. フロントエンドの設定 — 次の 3 つのファイルが必要です。いずれかを省略すると、新しいスコープはプロジェクトロール設定 UI に表示されません。
    • packages/frontend/@n8n/stores/src/rbac.store.ts — scopesByResourceId に <resource>: {} を追加します(追加しないと型チェックに失敗します)。
    • packages/frontend/editor-ui/src/features/roles/project/projectRoleScopes.ts — SCOPE_TYPES にリソースを追加します(ページ上でリソースグループが表示される順序です)。
    • packages/frontend/@n8n/i18n/src/locales/en.json — すべての操作について、projectRoles.<resource>:<op>(列ラベル)と projectRoles.<resource>:<op>.tooltip(ホバー時の説明)に加え、projectRoles.type.<resource>(グループ見出し)を追加します。
  8. スナップショット — packages/@n8n/permissions/src/__tests__/__snapshots__/scope-information.test.ts.snap を更新し、新しい <resource>:* エントリーを追加します。

DB のマイグレーションは不要です。AuthRolesService.init() が起動のたびにスコープ/ロールを同期します。UI で作成済みのカスタムチームロールは自動更新されません。PR の説明にその旨を記載してください。

公開/認証不要のルート

{ skipAuth: true } は認証ミドルウェアをスキップするため、req.user は undefined になります。そこに @ProjectScope を追加すると、すべての呼び出しが 401 になります。サードパーティの Webhook や署名付きコールバックなどの公開ルートでは、次の対応が必要です。

  1. スコープデコレーターを付けないでください。
  2. ハンドラー内で署名/HMAC 検証(またはルート固有の別の仕組み)を使って認証してください。
  3. スコープを適用しない理由をコメントに記載し、後続のレビュー担当者が「修正」しようとしないようにしてください。

例:

// Third-party webhook callback: do not add @ProjectScope. Auth happens
// via per-platform signature verification inside webhookHandler, and
// :projectId is unused in the (agentId, platform) lookup.
@Post('/:agentId/webhooks/:platform', { skipAuth: true, allowBots: true })
async handleWebhook(...) { ... }

ルートメタデータテストで検証する

今後、スコープなしでルートが追加された場合に失敗する回帰テストを追加してください。ControllerRegistryMetadata を使ってコントローラーのすべてのルートを反復し、ゲートをアサートしてください。

import { ControllerRegistryMetadata } from '@n8n/decorators';
import { Container } from '@n8n/di';
import { WidgetsController } from '../widgets.controller';

const UNAUTHENTICATED_HANDLERS = new Set<string>(); // add public handler names here

const metadata = Container.get(ControllerRegistryMetadata).getControllerMetadata(
  WidgetsController as never,
);
const routeCases = Array.from(metadata.routes.entries()).map(([handlerName, route]) => ({
  handlerName, route,
}));

describe('WidgetsController route access scopes', () => {
  it.each(routeCases)(
    '$handlerName is gated by a project-scoped widget:* check',
    ({ handlerName, route }) => {
      if (UNAUTHENTICATED_HANDLERS.has(handlerName)) {
        expect(route.accessScope).toBeUndefined();
        expect(route.skipAuth).toBe(true);
        return;
      }
      expect(route.accessScope).toBeDefined();
      expect(route.accessScope?.globalOnly).toBe(false);
      expect(route.accessScope?.scope.startsWith('widget:')).toBe(true);
    },
  );
});

多層防御(引き続き必須)

ハンドラーが下流の呼び出しを通じてデータを漏えいさせる場合、デコレーターだけでは不十分です。サービス/リポジトリのメソッドでも、引き続き projectId でフィルタリング(または findByUser のようなユーザースコープのヘルパーを使用)してください。デコレーターは この URL を呼び出せるユーザー を制限し、サービスは ユーザーが読み取れるデータ を制限します。必ず両方を適用してください。

参考パターン

  • プロジェクトスコープの CRUD:packages/cli/src/workflows/workflows.controller.ts、packages/cli/src/credentials/credentials.controller.ts、packages/cli/src/modules/data-table/data-table.controller.ts。
  • グローバルとプロジェクトの混在:packages/cli/src/controllers/project.controller.ts。

コメント