supabase-audit-rls について
supabase-audit-rlsは、SupabaseのRow Level Security(RLS)ポリシーを確認するセキュリティ監査スキルです。アクセス制御や一般的な回避手法をテストし、発見事項、ログ、証拠を作業中に記録します。
主な機能
- 01RLSの有効化とポリシー設定の確認
- 02未認証アクセスとユーザー間アクセスのテスト
- 03フィルター、結合、RPCによる回避の確認
- 04影響の説明とSQL修正案の提示
- 05監査情報、ログ、証拠の逐次記録
活用シーン
- 01SupabaseプロジェクトのテーブルのRLS監査
- 02許可されたデータだけにアクセスできるか検証
- 03結合やRPCを介したアクセス回避の調査
- 04追跡可能なセキュリティ監査記録の作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yoanbernabeu/ supabase-pentest-skills --skill supabase-audit-rlsインストール後、Claude Code で/supabase-audit-rlsを入力して読み込まれたか確認します。
詳細
RLS ポリシー監査
🔴 重大:コンテキストファイルを段階的に更新する必要があります
コンテキストファイルには、最後にまとめてではなく、作業を進めながら書き込む必要があります。
- 各発見事項の直後に、
.sb-pentest-context.jsonに書き込んでください- 各テストの前後に、
.sb-pentest-audit.logに記録してください- スキルの完了までファイルの更新を待たないでください
- スキルがクラッシュまたは中断された場合でも、それまでのすべての発見事項がすでに保存されている必要があります
これは必須です。段階的な書き込みを怠ると、重大なエラーになります。
このスキルでは、一般的な脆弱性や設定ミスがないか、行レベルセキュリティ(RLS)ポリシーをテストします。
このスキルを使用するタイミング
- テーブル内のデータ漏えいを発見した後
- RLS ポリシーが正しく実装されていることを確認する場合
- 一般的な RLS バイパス手法をテストする場合
- 包括的なセキュリティ監査の一環として
前提条件
- テーブルの一覧があること
- Anon キーを利用できること
- 可能であれば、認証済みユーザーのトークンでもテストすること
RLS を理解する
Supabase/PostgreSQL の行レベルセキュリティ:
-- Enable RLS on a table
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Create a policy
CREATE POLICY "Users see own posts"
ON posts FOR SELECT
USING (auth.uid() = author_id);
RLS が有効でもポリシーが存在しない場合、すべてのアクセスがブロックされます。
一般的な RLS の問題
| 問題 | 説明 | 重大度 |
|---|---|---|
| RLS が無効 | テーブルに RLS による保護がない | P0 |
| ポリシーの不足 | RLS は有効だが、SELECT ポリシーがない | さまざま |
| 過度に許可的 | ポリシーが過剰なアクセスを許可する | P0-P1 |
| 操作の不足 | SELECT ポリシーはあるが、INSERT/UPDATE/DELETE ポリシーがない | P1 |
| USING と WITH CHECK | 読み取りは許可されるが、書き込みの制御に一貫性がない | P1 |
テスト項目
このスキルでは、以下の一般的なバイパスシナリオをテストします。
1. 未認証アクセス
GET /rest/v1/users?select=*
# No Authorization header or with anon key only
2. ユーザー間のアクセス
# As user A, try to access user B's data
GET /rest/v1/orders?user_id=eq.[user-b-id]
Authorization: Bearer [user-a-token]
3. フィルターのバイパス
# Try to bypass filters with OR conditions
GET /rest/v1/posts?or=(published.eq.true,published.eq.false)
4. JOIN の悪用
# Try to access data through related tables
GET /rest/v1/comments?select=*,posts(*)
5. RPC のバイパス
# Check if RPC functions bypass RLS
POST /rest/v1/rpc/get_all_users
使用方法
基本的な RLS 監査
Audit RLS policies on my Supabase project
特定のテーブル
Test RLS on the users table
認証済みユーザーを使用
Test RLS policies using this user token: eyJ...
出力形式
═══════════════════════════════════════════════════════════
RLS POLICY AUDIT
═══════════════════════════════════════════════════════════
Project: abc123def.supabase.co
Tables Audited: 8
─────────────────────────────────────────────────────────
RLS Status by Table
─────────────────────────────────────────────────────────
1. users
RLS Enabled: ❌ NO
Status: 🔴 P0 - NO RLS PROTECTION
All operations allowed without restriction!
Test Results:
├── Anon SELECT: ✓ Returns all 1,247 rows
├── Anon INSERT: ✓ Succeeds (tested with rollback)
├── Anon UPDATE: ✓ Would succeed
└── Anon DELETE: ✓ Would succeed
Immediate Fix:
```sql
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users see own data"
ON users FOR ALL
USING (auth.uid() = id);
```
2. posts
RLS Enabled: ✅ YES
Policies Found: 2
Status: ✅ PROPERLY CONFIGURED
Policies:
├── "Public sees published" (SELECT)
│ └── USING: (published = true)
└── "Authors manage own" (ALL)
└── USING: (auth.uid() = author_id)
Test Results:
├── Anon SELECT: Only published posts (correct)
├── Anon INSERT: ❌ Blocked (correct)
├── Cross-user access: ❌ Blocked (correct)
└── Filter bypass: ❌ Blocked (correct)
3. orders
RLS Enabled: ✅ YES
Policies Found: 1
Status: 🟠 P1 - PARTIAL ISSUE
Policies:
└── "Users see own orders" (SELECT)
└── USING: (auth.uid() = user_id)
Issue Found:
├── No INSERT policy - users can't create orders via API
├── No UPDATE policy - users can't modify their orders
└── This may be intentional (orders via Edge Functions)
Recommendation: Document if intentional, or add policies:
```sql
CREATE POLICY "Users insert own orders"
ON orders FOR INSERT
WITH CHECK (auth.uid() = user_id);
```
4. comments
RLS Enabled: ✅ YES
Policies Found: 2
Status: 🟠 P1 - BYPASS POSSIBLE
Policies:
├── "Anyone can read" (SELECT)
│ └── USING: (true) ← Too permissive
└── "Users comment on posts" (INSERT)
└── WITH CHECK: (auth.uid() = user_id)
Issue Found:
└── SELECT policy allows reading all comments
including user_id, enabling user correlation
Recommendation:
```sql
-- Use a view to hide user_id
CREATE VIEW public.comments_public AS
SELECT id, post_id, content, created_at FROM comments;
```
5. settings
RLS Enabled: ❌ NO
Status: 🔴 P0 - NO RLS PROTECTION
Contains sensitive configuration!
Immediate action required.
─────────────────────────────────────────────────────────
Summary
─────────────────────────────────────────────────────────
RLS Disabled: 2 tables (users, settings) ← CRITICAL
RLS Enabled: 6 tables
├── Properly Configured: 3
├── Partial Issues: 2
└── Major Issues: 1
Bypass Tests:
├── Unauthenticated access: 2 tables vulnerable
├── Cross-user access: 0 tables vulnerable
├── Filter bypass: 0 tables vulnerable
└── Join exploitation: 1 table allows data leakage
═══════════════════════════════════════════════════════════
コンテキスト出力
{
"rls_audit": {
"timestamp": "2025-01-31T10:45:00Z",
"tables_audited": 8,
"summary": {
"rls_disabled": 2,
"rls_enabled": 6,
"properly_configured": 3,
"partial_issues": 2,
"major_issues": 1
},
"findings": [
{
"table": "users",
"rls_enabled": false,
"severity": "P0",
"issue": "No RLS protection",
"operations_exposed": ["SELECT", "INSERT", "UPDATE", "DELETE"]
},
{
"table": "comments",
"rls_enabled": true,
"severity": "P1",
"issue": "Overly permissive SELECT policy",
"detail": "user_id exposed enabling correlation"
}
]
}
}
一般的な RLS パターン
良い例:ユーザーが自分のデータを所有する
CREATE POLICY "Users own their data"
ON user_data FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
良い例:公開読み取り、認証済みユーザーによる書き込み
-- Anyone can read
CREATE POLICY "Public read" ON posts
FOR SELECT USING (published = true);
-- Only authors can write
CREATE POLICY "Author write" ON posts
FOR INSERT WITH CHECK (auth.uid() = author_id);
CREATE POLICY "Author update" ON posts
FOR UPDATE USING (auth.uid() = author_id);
悪い例:USING (true) の使用
-- ❌ Too permissive
CREATE POLICY "Anyone" ON secrets
FOR SELECT USING (true);
悪い例:WITH CHECK の記述忘れ
-- ❌ Users can INSERT any user_id
CREATE POLICY "Insert" ON posts
FOR INSERT WITH CHECK (true); -- Should check user_id!
RLS バイパスのドキュメント
バイパスが見つかった場合、このスキルでは以下を提示します。
- 脆弱性の説明
- クエリの概念実証
- 影響の評価
- SQL コードによる修正方法
- ドキュメントへのリンク
必須:コンテキストファイルの段階的な更新
⚠️ このスキルでは、実行中にトラッキングファイルを段階的に更新する必要があります。最後にまとめて更新してはいけません。
重要なルール:作業を進めながら書き込む
書き込みを最後にまとめて行わないでください。代わりに、次の手順に従ってください。
- 各テーブルのテスト前 → 操作を
.sb-pentest-audit.logに記録する - RLS に関する各発見事項の後 → すぐに
.sb-pentest-context.jsonを更新する - 各テストの完了後 → 結果を
.sb-pentest-audit.logに記録する
これにより、スキルが中断、クラッシュ、またはタイムアウトしても、その時点までのすべての発見事項が保持されます。
必須アクション(段階的に実施)
-
結果を
.sb-pentest-context.jsonに記録します。{ "rls_audit": { "timestamp": "...", "tables_audited": 8, "summary": { "rls_disabled": 2, ... }, "findings": [ ... ] } } -
.sb-pentest-audit.logに記録します。[TIMESTAMP] [supabase-audit-rls] [START] Auditing RLS policies [TIMESTAMP] [supabase-audit-rls] [FINDING] P0: users table has no RLS [TIMESTAMP] [supabase-audit-rls] [CONTEXT_UPDATED] .sb-pentest-context.json updated -
ファイルが存在しない場合は、書き込みの前に作成します。
コンテキストファイルを更新しないことは許されません。
必須:証拠の収集
📁 証拠ディレクトリ: .sb-pentest-evidence/03-api-audit/rls-tests/
作成する証拠ファイル
| ファイル | 内容 |
|---|---|
rls-tests/[table]-anon.json | 匿名アクセスのテスト結果 |
rls-tests/[table]-auth.json | 認証済みアクセスのテスト結果 |
rls-tests/cross-user-test.json | ユーザー間アクセスの試行 |
証拠の形式(RLS バイパス)
{
"evidence_id": "RLS-001",
"timestamp": "2025-01-31T10:25:00Z",
"category": "api-audit",
"type": "rls_test",
"severity": "P0",
"table": "users",
"rls_enabled": false,
"tests": [
{
"test_name": "anon_select",
"description": "Anonymous user SELECT access",
"request": {
"curl_command": "curl -s '$URL/rest/v1/users?select=*&limit=5' -H 'apikey: $ANON_KEY'"
},
"response": {
"status": 200,
"rows_returned": 5,
"total_accessible": 1247
},
"result": "VULNERABLE",
"impact": "All user data accessible without authentication"
},
{
"test_name": "anon_insert",
"description": "Anonymous user INSERT access",
"request": {
"curl_command": "curl -X POST '$URL/rest/v1/users' -H 'apikey: $ANON_KEY' -d '{...}'"
},
"response": {
"status": 201
},
"result": "VULNERABLE",
"impact": "Can create arbitrary user records"
}
],
"remediation_sql": "ALTER TABLE users ENABLE ROW LEVEL SECURITY;\nCREATE POLICY \"Users see own data\" ON users FOR SELECT USING (auth.uid() = id);"
}
curl-commands.sh に追加
# === RLS BYPASS TESTS ===
# Test anon access to users table
curl -s "$SUPABASE_URL/rest/v1/users?select=*&limit=5" \
-H "apikey: $ANON_KEY" -H "Authorization: Bearer $ANON_KEY"
# Test filter bypass
curl -s "$SUPABASE_URL/rest/v1/posts?or=(published.eq.true,published.eq.false)" \
-H "apikey: $ANON_KEY"
関連スキル
supabase-audit-tables-list— まずテーブルを一覧表示しますsupabase-audit-tables-read— 実際のデータ漏えいを確認しますsupabase-audit-rpc— RPC 関数は RLS をバイパスする可能性がありますsupabase-report— セキュリティレポート全体