mcpField
S
supabase-extract-service-key

supabase-extract-service-key

github.com/yoanbernabeuセキュリティ71486 回インストール39 週間前に更新
リポジトリ

supabase-extract-service-key について

supabase-extract-service-keyは、クライアント側コードにSupabaseのservice_roleキーが露出していないか調べるセキュリティ監査スキルです。キーのパターンとソースマップを確認し、監査結果と証拠を記録します。

主な機能

  1. 01クライアントコード内のservice_roleキー検出
  2. 02一般的なサービスキー変数名の確認
  3. 03公開されたソースマップの検査
  4. 04発見事項と証拠の記録

活用シーン

  1. 01Webアプリのサービスキー漏えいを監査
  2. 02デプロイ前にクライアントバンドルを確認
  3. 03ソースマップからのキー漏えいを調査

インストール

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

$npx skills add https://github.com/yoanbernabeu/supabase-pentest-skills --skill supabase-extract-service-key

インストール後、Claude Code で/supabase-extract-service-keyを入力して読み込まれたか確認します。

詳細

Supabase サービスキーの検出

🔴 重大:コンテキストファイルを段階的に更新する必要があります

コンテキストファイルには、最後にまとめてではなく、作業しながら必ず書き込んでください。

  • 発見するたびに .sb-pentest-context.json に直ちに書き込んでください
  • 各アクションの前後に .sb-pentest-audit.log に記録してください
  • スキルの完了までファイルの更新を待たないでください
  • スキルがクラッシュしたり中断されたりしても、それまでの発見事項がすべて保存されている必要があります

これは任意ではありません。段階的に書き込まないことは重大なエラーです。

このスキルは、service_role キー(管理者キー)がクライアント側のコードで誤って公開されていないかを検出します。

このスキルを使用するタイミング

  • すべてのセキュリティ監査の一環として(これは重要です)
  • 本番環境にデプロイする前のコードレビュー時
  • よくあるこの誤りを確認するため、Supabase の使用を検出した後

前提条件

  • 対象アプリケーションにアクセスできること
  • Supabase の検出が完了していること(必要に応じて自動的に実行されます)

これが重大である理由

service_role キーは、すべての Row Level Security(RLS)ポリシーをバイパスします。公開された場合:

影響説明
🔴 DB への完全なアクセスすべてのテーブルの全データを読み取り/書き込み/削除できます
🔴 認証のバイパス認証なしで全ユーザーデータにアクセスできます
🔴 ストレージへのアクセスすべてのバケット内の全ファイルを読み取り/書き込みできます
🔴 ユーザーのなりすまし任意のユーザーのトークンを生成できます

これは P0(重大)の検出事項であり、直ちに対応する必要があります。

サービスキーと匿名キーの比較

項目匿名キーサービスキー
ロールクレーム"role": "anon""role": "service_role"
RLS✅ RLS を遵守❌ RLS をバイパス
クライアント側✅ 想定どおり❌ 絶対に使用しない
サーバー側✅ 使用可能✅ 使用するべき

検出パターン

このスキルは次のパターンを検索します。

1. service_role クレームを持つキー
// Decoded JWT payload contains:
{
  "role": "service_role",  // ❌ CRITICAL if in client code
  "iss": "supabase",
  "ref": "abc123def"
}
2. 変数名
// Common naming patterns
SUPABASE_SERVICE_KEY
SUPABASE_SERVICE_ROLE_KEY
SUPABASE_ADMIN_KEY
SUPABASE_SECRET_KEY
SERVICE_ROLE_KEY
3. 誤った公開
// Sometimes exposed alongside anon key
const keys = {
  anon: 'eyJ...',
  service: 'eyJ...'  // ❌ Should not be here
}

使用方法

基本チェック
Check for service key leak on https://myapp.example.com
詳細スキャン
Deep scan for service key exposure on https://myapp.example.com

出力形式

サービスキーが見つからない場合(良好)
═══════════════════════════════════════════════════════════
 SERVICE KEY CHECK
═══════════════════════════════════════════════════════════

 Status: ✅ No service_role key detected in client code

 Scanned:
 ├── HTML source: Clean
 ├── JavaScript bundles: 5 files, 2.3MB analyzed
 ├── Inline scripts: 12 blocks checked
 └── Source maps: Not exposed (good)

 JWT Analysis:
 └── 1 key found, confirmed role=anon (safe)

 Result: PASS - No critical key exposure
═══════════════════════════════════════════════════════════
サービスキーが見つかった場合(重大)
═══════════════════════════════════════════════════════════
 🔴 CRITICAL: SERVICE KEY EXPOSED
═══════════════════════════════════════════════════════════

 Severity: P0 - CRITICAL
 Status: ❌ service_role key found in client-side code!

 ⚠️  IMMEDIATE ACTION REQUIRED ⚠️

 Exposed Key:
 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBh
 YmFzZSIsInJlZiI6ImFiYzEyM2RlZiIsInJvbGUiOiJzZXJ2aWNlX3
 JvbGUiLCJpYXQiOjE2NDAwMDAwMDAsImV4cCI6MTk1NTM2MDAwMH0
 .xxxxxxxxxxxxx

 Location:
 └── /static/js/admin.chunk.js (line 89)
     const SUPABASE_KEY = 'eyJhbG...'  // Used in createClient()

 Decoded Payload:
 ├── role: service_role ← CRITICAL
 ├── ref: abc123def
 └── exp: 2031-12-20

 Impact Assessment:
 ├── 🔴 Full database access possible
 ├── 🔴 All RLS policies bypassed
 ├── 🔴 All user data exposed
 └── 🔴 All storage buckets accessible

 ═══════════════════════════════════════════════════════════
 IMMEDIATE REMEDIATION STEPS
 ═══════════════════════════════════════════════════════════

 1. ROTATE THE KEY NOW
    → Supabase Dashboard > Settings > API > Regenerate service_role key

 2. REMOVE FROM CLIENT CODE
    → Delete the key from your source code
    → Redeploy your application

 3. AUDIT FOR ABUSE
    → Check Supabase logs for unauthorized access
    → Review database for unexpected changes

 4. USE EDGE FUNCTIONS
    → Move privileged operations to Edge Functions
    → Client calls Edge Function, which uses service key server-side

 Documentation:
 → https://supabase.com/docs/guides/api/api-keys
 → https://supabase.com/docs/guides/functions

═══════════════════════════════════════════════════════════

コンテキスト出力

.sb-pentest-context.json に保存されます。

{
  "findings": [
    {
      "id": "SERVICE_KEY_EXPOSED",
      "severity": "P0",
      "title": "Service Role Key Exposed in Client Code",
      "description": "The service_role key was found in client-side JavaScript",
      "location": {
        "file": "/static/js/admin.chunk.js",
        "line": 89
      },
      "evidence": {
        "key_prefix": "eyJhbGciOiJIUzI1NiI...",
        "role": "service_role",
        "project_ref": "abc123def"
      },
      "remediation": {
        "immediate": "Rotate key in Supabase Dashboard",
        "long_term": "Move to Edge Functions",
        "docs": "https://supabase.com/docs/guides/api/api-keys"
      }
    }
  ],
  "supabase": {
    "service_key_exposed": true,
    "service_key_location": "/static/js/admin.chunk.js:89"
  }
}

ソースマップの確認

このスキルは、キーが漏えいしている可能性のあるソースマップも確認します。

Source Maps Analysis:
├── main.js.map: ❌ Exposed (may contain secrets)
├── vendor.js.map: ❌ Exposed
└── Recommendation: Disable source maps in production

To check source maps content:
→ Add .map to JS URLs: /static/js/main.js.map

よくある原因

原因解決策
環境変数の誤り匿名キーにのみ NEXT_PUBLIC_ を使用してください
コピー&ペーストの誤り使用しているキーを再確認してください
デバッグコードの残存本番ビルドの前に削除してください
バンドラーの設定ミスサービスキーの環境変数が含まれていないことを確認してください

修正コードの例

修正前(誤り)
// ❌ WRONG - Service key in client
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_SERVICE_KEY  // ❌ NEVER DO THIS
)
修正後(正しい)
// ✅ CORRECT - Only anon key in client
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY  // ✅ Safe for client
)

// For privileged operations, call an Edge Function:
const { data } = await supabase.functions.invoke('admin-action', {
  body: { action: 'delete-user', userId: '123' }
})
Edge Function(サーバー側)
// supabase/functions/admin-action/index.ts
import { createClient } from '@supabase/supabase-js'

Deno.serve(async (req) => {
  // ✅ Service key only on server
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL'),
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')  // ✅ Safe on server
  )

  // Perform privileged operation
  // ...
})

必須:コンテキストファイルの段階的な更新

⚠️ このスキルは、最後にまとめてではなく、実行中に追跡ファイルを段階的に更新する必要があります。

重要なルール:作業しながら書き込む

最後にすべての書き込みをまとめて行わないでください。代わりに、次の手順に従ってください。

  1. アクションを開始する前に → .sb-pentest-audit.log にアクションを記録します
  2. 発見するたびに → 直ちに .sb-pentest-context.json を更新します
  3. 重要な手順のたびに → .sb-pentest-audit.log に完了を記録します

これにより、スキルが中断されたり、クラッシュしたり、タイムアウトしたりしても、その時点までのすべての発見事項が保持されます。

必須アクション(段階的に実行)
  1. 発見事項を .sb-pentest-context.json に更新します。

    {
      "supabase": {
        "service_key_exposed": true/false,
        "service_key_location": "path:line"
      },
      "findings": [
        {
          "id": "SERVICE_KEY_EXPOSED",
          "severity": "P0",
          "title": "Service Role Key Exposed",
          ...
        }
      ]
    }
    
  2. .sb-pentest-audit.log に記録します。

    [TIMESTAMP] [supabase-extract-service-key] [START] Checking for service key exposure
    [TIMESTAMP] [supabase-extract-service-key] [CRITICAL] Service key EXPOSED at path:line
    [TIMESTAMP] [supabase-extract-service-key] [CONTEXT_UPDATED] .sb-pentest-context.json updated
    
  3. ファイルが存在しない場合は、書き込む前に作成します。

コンテキストファイルを更新しないことは許容されません。

必須:証拠の収集

📁 証拠ディレクトリ: .sb-pentest-evidence/02-extraction/service-key-exposure/

作成する証拠ファイル(サービスキーが見つかった場合)
ファイル内容
service-key-exposure/location.txtファイルパスと行番号
service-key-exposure/decoded-payload.jsonservice_role であることを証明するデコード済み JWT
service-key-exposure/code-snippet.txtコードのコンテキスト(秘匿化済み)
証拠の形式(P0 の検出事項)
{
  "evidence_id": "EXT-SVC-001",
  "timestamp": "2025-01-31T10:10:00Z",
  "category": "extraction",
  "type": "service_key_exposure",
  "severity": "P0",
  "finding_id": "P0-001",

  "key_data": {
    "key_prefix": "eyJhbGciOiJIUzI1NiI...",
    "key_suffix": "...xxxx",
    "role": "service_role"
  },

  "decoded_payload": {
    "iss": "supabase",
    "ref": "abc123def",
    "role": "service_role",
    "iat": "2021-12-20T00:00:00Z",
    "exp": "2031-12-20T00:00:00Z"
  },

  "location": {
    "file": "/static/js/admin.chunk.js",
    "line": 89,
    "context": "const SUPABASE_KEY = 'eyJhbG...' // [REDACTED]"
  },

  "impact": {
    "rls_bypass": true,
    "full_db_access": true,
    "auth_users_access": true,
    "storage_access": true
  },

  "curl_command": "curl -X GET 'https://abc123def.supabase.co/rest/v1/users' -H 'apikey: [SERVICE_KEY]' -H 'Authorization: Bearer [SERVICE_KEY]'"
}
timeline.md に追加(P0)
## [TIMESTAMP] - 🔴 P0 CRITICAL: Service Role Key Exposed
- Service role key found in client-side code
- Location: [file]:[line]
- Impact: Full database access, RLS bypass
- Evidence: `02-extraction/service-key-exposure/`
- **IMMEDIATE ACTION REQUIRED**

関連スキル

  • supabase-extract-anon-key — 想定される anon key を抽出します
  • supabase-audit-tables-read — アクセス可能なデータをテストします
  • supabase-report — この検出結果を含む完全なレポートを生成します

コメント