mcpField
S
supabase-audit-functions

supabase-audit-functions

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

supabase-audit-functions について

Supabase Edge Functionsを発見し、セキュリティ上の問題を検査するスキルです。Supabaseのセキュリティ監査向けに、関数の発見、認証、入力検証、エラー情報の漏えいを調べ、監査記録と証拠を逐次保存します。

主な機能

  1. 01公開されたEdge Functionsの発見
  2. 02JWT認証要件の確認
  3. 03入力検証とインジェクションリスクのテスト
  4. 04エラー情報漏えいの確認
  5. 05監査コンテキストと証拠の逐次保存

活用シーン

  1. 01アクセス可能なEdge Functionsの一覧化
  2. 02関数のJWT必須設定の確認
  3. 03入力処理とアクセス制御のテスト
  4. 04関数エラーによる情報漏えいの確認

インストール

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

$npx skills add https://github.com/yoanbernabeu/supabase-pentest-skills --skill supabase-audit-functions

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

詳細

Edge Functions の監査

🔴 重要:段階的なファイル更新が必須です

コンテキストファイルには、最後だけでなく、作業を進めながら書き込む必要があります。

  • 各関数のテスト直後に、.sb-pentest-context.json に直ちに書き込んでください
  • 各関数のテストの前後に、.sb-pentest-audit.log に記録してください
  • このスキルが完了するまでファイルの更新を待たないでください
  • スキルがクラッシュまたは中断された場合でも、それまでの調査結果がすべて保存されている必要があります

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

このスキルでは、Supabase Edge Functions を検出し、セキュリティ上の問題をテストします。

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

  • 公開されている Edge Functions を検出するとき
  • 関数に認証要件があるかテストするとき
  • 入力検証の問題を確認するとき
  • 包括的なセキュリティ監査の一環として

前提条件

  • Supabase の URL を利用できること
  • 検出が完了していること

Edge Functions について

Supabase Edge Functions は Deno ベースのサーバーレス関数です。

https://[project].supabase.co/functions/v1/[function-name]
セキュリティの観点考慮事項
認証関数では JWT を必須にすることも、公開することもできます
CORSクロスオリジンアクセス制御
入力検証ユーザー入力の処理
シークレット環境変数の露出

実施するテスト

テスト目的
関数の検出公開されている関数を見つける
認証要件JWT が必須か確認する
入力検証インジェクションをテストする
エラー処理情報漏えいを確認する

使用方法

基本的な関数監査
Audit Edge Functions on my Supabase project
特定の関数をテストする
Test the process-payment Edge Function for security issues

出力形式

═══════════════════════════════════════════════════════════
 EDGE FUNCTIONS AUDIT
═══════════════════════════════════════════════════════════

 Project: abc123def.supabase.co
 Endpoint: https://abc123def.supabase.co/functions/v1/

 ─────────────────────────────────────────────────────────
 Function Discovery
 ─────────────────────────────────────────────────────────

 Discovery Method: Common name enumeration + client code analysis

 Functions Found: 5

 ─────────────────────────────────────────────────────────
 1. hello-world
 ─────────────────────────────────────────────────────────

 Endpoint: /functions/v1/hello-world
 Method: GET, POST

 Authentication Test:
 ├── Without JWT: ✅ 200 OK
 └── Status: ℹ️ Public function (no auth required)

 Response:
 ```json
 {"message": "Hello, World!"}

評価:✅ 適切 シンプルな公開エンドポイントで、機密性の高い操作はありません。

───────────────────────────────────────────────────────── 2. process-payment ─────────────────────────────────────────────────────────

エンドポイント:/functions/v1/process-payment メソッド:POST

認証テスト: ├── JWT なし:❌ 401 Unauthorized ├── 有効な JWT あり:✅ 200 OK └── ステータス:✅ 認証が必要

入力検証テスト: ├── amount がない:❌ 400 Bad Request(良好) ├── amount が負の値:❌ 400 Bad Request(良好) ├── amount が文字列:❌ 400 Bad Request(良好) └── 有効な入力:✅ 200 OK

エラーレスポンステスト: ├── エラー形式:汎用メッセージ(良好) └── スタックトレース:❌ 公開されていない(良好)

評価:✅ 適切に保護されています 認証を要求し、入力を検証し、安全にエラーを処理します。

───────────────────────────────────────────────────────── 3. get-user-data ─────────────────────────────────────────────────────────

エンドポイント:/functions/v1/get-user-data メソッド:GET

認証テスト: ├── JWT なし:❌ 401 Unauthorized └── ステータス:✅ 認証が必要

認可テスト: ├── 自分のデータをリクエスト:✅ 200 OK ├── 他のユーザーのデータをリクエスト:✅ 200 OK ← 🔴 P0! └── ステータス:🔴 アクセス制御の不備

テスト:

# As user A, request user B's data
curl https://abc123def.supabase.co/functions/v1/get-user-data?user_id=user-b-id \
  -H "Authorization: Bearer [user-a-token]"

# Returns user B's data!

検出事項:🔴 P0 - IDOR 脆弱性 この関数は user_id パラメーターを受け付けますが、 認証済みユーザーが自分自身のデータをリクエストしているかどうかを検証していません。

修正:

// In Edge Function
const { user_id } = await req.json();
const jwt_user = getUser(req); // From JWT

// Verify ownership
if (user_id !== jwt_user.id) {
  return new Response('Forbidden', { status: 403 });
}

───────────────────────────────────────────────────────── 4. admin-panel ─────────────────────────────────────────────────────────

エンドポイント:/functions/v1/admin-panel メソッド:GET, POST

認証テスト: ├── JWT なし:❌ 401 Unauthorized ├── 一般ユーザーの JWT あり:✅ 200 OK ← 🔴 P0! └── ステータス:🔴 ロールチェックの欠如

検出事項:🔴 P0 - 権限昇格 管理者向け関数に、認証済みであれば誰でもアクセスできます。 関数コードでロールが検証されていません。

修正:

// Verify admin role
const user = getUser(req);
const { data: profile } = await supabase
  .from('profiles')
  .select('is_admin')
  .eq('id', user.id)
  .single();

if (!profile?.is_admin) {
  return new Response('Forbidden', { status: 403 });
}

───────────────────────────────────────────────────────── 5. webhook-handler ─────────────────────────────────────────────────────────

エンドポイント:/functions/v1/webhook-handler メソッド:POST

認証テスト: ├── JWT なし:✅ 200 OK(Webhook では想定どおり) └── ステータス:ℹ️ 公開(Webhook エンドポイントは通常公開されています)

Webhook セキュリティテスト: ├── 署名の検証:⚠️ テスト不可(有効な署名が必要) └── レート制限:不明

エラーレスポンステスト:

{
  "error": "Invalid signature",
  "expected": "sha256=abc123...",
  "received": "sha256=xyz789..."
}

検出事項:🟠 P1 - 情報漏えい エラーレスポンスから、想定される署名形式が明らかになります。 これにより、攻撃者が検証メカニズムを把握しやすくなる可能性があります。

修正:

// Generic error, log details server-side
if (!validSignature) {
  console.error(`Invalid signature: expected ${expected}, got ${received}`);
  return new Response('Unauthorized', { status: 401 });
}

───────────────────────────────────────────────────────── 概要 ─────────────────────────────────────────────────────────

検出された関数:5

セキュリティ評価: ├── ✅ 安全:2(hello-world、process-payment) ├── 🔴 P0:2(get-user-data の IDOR、admin-panel の権限昇格) └── 🟠 P1:1(webhook-handler の情報漏えい)

重大な検出事項:

  1. get-user-data の IDOR - すべてのユーザーが、任意のユーザーのデータにアクセスできます
  2. admin-panel にロールチェックがない - すべてのユーザーが管理者として扱われます

優先対応事項:

  1. get-user-data を修正し、リクエストされたデータの所有者がユーザー本人であることを検証する
  2. admin-panel に管理者ロールの検証を追加する
  3. webhook-handler のエラーメッセージを修正する

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


## Common Function Vulnerabilities

| Vulnerability | Description | Severity |
|---------------|-------------|----------|
| No auth | Function accessible without JWT | P0-P2 |
| IDOR | User can access other users' data | P0 |
| Missing role check | Regular user accesses admin functions | P0 |
| Input injection | User input not validated | P0-P1 |
| Info disclosure | Errors reveal internal details | P1-P2 |
| CORS misconfigured | Accessible from unintended origins | P1-P2 |

## Function Discovery Methods

### 1. Client Code Analysis

```javascript
// Look for function invocations in client code
supabase.functions.invoke('function-name', {...})
fetch('/functions/v1/function-name', {...})
2. 一般的な名前の列挙

テストした関数名:

  • hello-world, hello, test
  • process-payment, payment, checkout
  • get-user-data, user, profile
  • admin, admin-panel, dashboard
  • webhook, webhook-handler, stripe-webhook
  • send-email, notify, notification
3. エラーレスポンスの分析
404 Not Found → Function doesn't exist
401 Unauthorized → Function exists, needs auth
200 OK → Function exists, accessible

コンテキスト出力

{
  "functions_audit": {
    "timestamp": "2025-01-31T14:30:00Z",
    "functions_found": 5,
    "findings": [
      {
        "function": "get-user-data",
        "severity": "P0",
        "vulnerability": "IDOR",
        "description": "Any authenticated user can access any user's data",
        "remediation": "Verify user owns requested resource"
      },
      {
        "function": "admin-panel",
        "severity": "P0",
        "vulnerability": "Privilege Escalation",
        "description": "No role check, any authenticated user is admin",
        "remediation": "Add admin role verification"
      }
    ]
  }
}

安全な関数のパターン

認証チェック
import { createClient } from '@supabase/supabase-js'

Deno.serve(async (req) => {
  // Get JWT from header
  const authHeader = req.headers.get('Authorization');
  if (!authHeader) {
    return new Response('Unauthorized', { status: 401 });
  }

  // Verify JWT with Supabase
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_ANON_KEY')!,
    { global: { headers: { Authorization: authHeader } } }
  );

  const { data: { user }, error } = await supabase.auth.getUser();
  if (error || !user) {
    return new Response('Unauthorized', { status: 401 });
  }

  // User is authenticated
  // ...
});
認可チェック(IDOR の防止)
// For user-specific resources
const requestedUserId = body.user_id;
const authenticatedUserId = user.id;

if (requestedUserId !== authenticatedUserId) {
  return new Response('Forbidden', { status: 403 });
}
ロールチェック(管理者)
// Check admin role
const { data: profile } = await supabase
  .from('profiles')
  .select('role')
  .eq('id', user.id)
  .single();

if (profile?.role !== 'admin') {
  return new Response('Forbidden', { status: 403 });
}
入力検証
import { z } from 'zod';

const PaymentSchema = z.object({
  amount: z.number().positive().max(10000),
  currency: z.enum(['usd', 'eur', 'gbp']),
  description: z.string().max(200).optional()
});

// Validate input
const result = PaymentSchema.safeParse(body);
if (!result.success) {
  return new Response(
    JSON.stringify({ error: 'Invalid input' }),
    { status: 400 }
  );
}

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

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

重要なルール:作業を進めながら記録する

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

  1. 各関数をテストする前に → 操作を .sb-pentest-audit.log に記録します
  2. 脆弱性が見つかるたびに → .sb-pentest-context.json を直ちに更新します
  3. 各関数のテスト完了後に → 結果を直ちに記録します

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

必須アクション(段階的に実施)
  1. 結果を .sb-pentest-context.json に更新します:

    {
      "functions_audit": {
        "timestamp": "...",
        "functions_found": 5,
        "findings": [ ... ]
      }
    }
    
  2. .sb-pentest-audit.log に記録します:

    [TIMESTAMP] [supabase-audit-functions] [START] Auditing Edge Functions
    [TIMESTAMP] [supabase-audit-functions] [FINDING] P0: IDOR in get-user-data
    [TIMESTAMP] [supabase-audit-functions] [CONTEXT_UPDATED] .sb-pentest-context.json updated
    
  3. ファイルが存在しない場合は、書き込む前に作成します。

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

必須:証拠の収集

📁 証拠ディレクトリ: .sb-pentest-evidence/07-functions-audit/

作成する証拠ファイル
ファイル内容
discovered-functions.json検出された Edge Functions の一覧
function-tests/[name].json関数ごとのテスト結果
証拠の形式(IDOR の脆弱性)
{
  "evidence_id": "FN-001",
  "timestamp": "2025-01-31T11:10:00Z",
  "category": "functions-audit",
  "type": "idor_vulnerability",
  "severity": "P0",

  "function": "get-user-data",
  "endpoint": "https://abc123def.supabase.co/functions/v1/get-user-data",

  "tests": [
    {
      "test_name": "auth_required",
      "request": {
        "method": "GET",
        "headers": {},
        "curl_command": "curl '$URL/functions/v1/get-user-data'"
      },
      "response": {"status": 401},
      "result": "PASS"
    },
    {
      "test_name": "idor_test",
      "description": "As user A, request user B's data",
      "request": {
        "method": "GET",
        "url": "$URL/functions/v1/get-user-data?user_id=user-b-id",
        "headers": {"Authorization": "Bearer [USER_A_TOKEN]"},
        "curl_command": "curl '$URL/functions/v1/get-user-data?user_id=user-b-id' -H 'Authorization: Bearer [USER_A_TOKEN]'"
      },
      "response": {
        "status": 200,
        "body": {"id": "user-b-id", "email": "[REDACTED]", "data": "[REDACTED]"}
      },
      "result": "VULNERABLE",
      "impact": "Any authenticated user can access any other user's data"
    }
  ],

  "remediation": "Add ownership check: if (user_id !== jwt_user.id) return 403"
}
証拠の形式(権限昇格)
{
  "evidence_id": "FN-002",
  "timestamp": "2025-01-31T11:15:00Z",
  "category": "functions-audit",
  "type": "privilege_escalation",
  "severity": "P0",

  "function": "admin-panel",

  "test": {
    "description": "Regular user accessing admin function",
    "request": {
      "method": "GET",
      "headers": {"Authorization": "Bearer [REGULAR_USER_TOKEN]"},
      "curl_command": "curl '$URL/functions/v1/admin-panel' -H 'Authorization: Bearer [REGULAR_USER_TOKEN]'"
    },
    "response": {
      "status": 200,
      "body": {"admin_data": "[REDACTED]"}
    },
    "result": "VULNERABLE",
    "impact": "Any authenticated user has admin access"
  }
}

関連スキル

  • supabase-audit-rpc — データベース関数(Edge Functions とは異なります)
  • supabase-audit-auth-config — 認証設定
  • supabase-report — 最終レポートに含めます

コメント