mcpField
S
supabase-audit-tables-read

supabase-audit-tables-read

github.com/yoanbernabeu安全71496 次安装39 周前更新
代码仓库

关于 supabase-audit-tables-read

用于测试 Supabase 表数据读取权限的安全审计技能,面向检查 RLS 策略效果和评估数据暴露程度的审计人员。它对列出的表执行只读查询,并按数据敏感度评估结果。

主要功能

  1. 01对表执行只读读取测试
  2. 02支持快速、抽样和计数模式
  3. 03按敏感数据类型评估严重性
  4. 04逐表保存结果并记录审计日志
  5. 05生成访问证据和 curl 示例

适用场景

  1. 01验证 RLS 是否阻止未授权读取
  2. 02确认暴露表实际可读取的数据
  3. 03在不获取行数据的情况下统计可访问记录

安装

复制下面的命令,在终端中运行。

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

安装后在 Claude Code 中输入/supabase-audit-tables-read确认已加载。

详细介绍

表数据访问测试

🔴 严重:必须渐进式更新文件

你必须边执行边写入上下文文件,而不是只在最后写入。

  • 每测试完一张表,立即写入 .sb-pentest-context.json
  • 每次测试前后都要记录到 .sb-pentest-audit.log
  • 不要等技能执行完毕后才更新文件
  • 如果技能崩溃或中断,之前的所有发现都必须已经保存

这不是可选项。不进行渐进式写入属于严重错误。

此技能尝试读取已暴露表中的数据,以确定实际可访问的信息。

何时使用此技能

  • 列出表之后,用于验证实际访问权限
  • 测试 RLS 策略是否有效
  • 评估数据暴露的严重程度
  • 准确记录可以检索哪些数据

前提条件

  • 已列出表(如有需要,将自动调用 supabase-audit-tables-list)
  • 有可用的 Anon key

工作原理

此技能会对每张已暴露的表执行 SELECT 查询:

GET https://[project].supabase.co/rest/v1/[table]?select=*&limit=5
Authorization: Bearer [anon-key]

**重要:**此操作为只读操作,不会修改或删除任何数据。

测试模式

模式描述查询
快速从每张表读取前 5 行?limit=5
抽样从各表随机抽样?limit=10&order=random
计数仅统计行数,不读取数据HEAD 请求

用法

基本读取测试
Test read access on exposed tables
仅快速计数
Count accessible rows in all tables (no data retrieval)
指定表
Test read access on the users table

输出格式

═══════════════════════════════════════════════════════════
 DATA ACCESS TEST RESULTS
═══════════════════════════════════════════════════════════

 Test Mode: Quick (5 rows per table)
 Tables Tested: 8

 ─────────────────────────────────────────────────────────
 Results by Table
 ─────────────────────────────────────────────────────────

 1. users
    Status: 🔴 P0 - DATA EXPOSED
    Rows Retrieved: 5 (of 1,247 total)
    Sample Data:
    ┌─────────────────────────────────────────────────────┐
    │ id: 550e8400-e29b-41d4-a716-446655440001           │
    │ email: [email protected] ← PII EXPOSED          │
    │ name: John Doe ← PII EXPOSED                       │
    │ avatar_url: https://...                            │
    │ created_at: 2025-01-15T10:30:00Z                   │
    └─────────────────────────────────────────────────────┘
    Finding: User emails and names accessible without auth

 2. profiles
    Status: 🟠 P1 - PARTIAL ACCESS
    Rows Retrieved: 5
    Note: Only public fields returned (RLS working partially)
    Columns Visible: id, bio, website
    Columns Blocked: user_id, social_links, private_notes

 3. posts
    Status: ✅ EXPECTED ACCESS
    Rows Retrieved: 5
    Note: Only published=true posts returned (RLS working)
    Data: Public content, appropriate access level

 4. orders
    Status: ✅ BLOCKED
    Response: 403 Forbidden
    Message: "new row violates row-level security policy"
    Note: RLS properly blocking access

 5. api_keys
    Status: ✅ BLOCKED
    Response: 403 Forbidden
    Note: RLS properly protecting secrets

 6. products
    Status: ✅ EXPECTED ACCESS
    Rows Retrieved: 5
    Note: Public catalog data, appropriate access

 7. comments
    Status: 🟠 P1 - MORE DATA THAN EXPECTED
    Rows Retrieved: 5
    Issue: user_id column exposed (can correlate to users)
    Recommendation: Use a view to hide user_id

 8. settings
    Status: 🔴 P0 - SENSITIVE DATA EXPOSED
    Rows Retrieved: 3
    Sample Data:
    ┌─────────────────────────────────────────────────────┐
    │ key: stripe_webhook_secret                          │
    │ value: whsec_xxxxxxxxxxxx ← SECRET EXPOSED         │
    └─────────────────────────────────────────────────────┘
    Finding: Application secrets in accessible table!

 ─────────────────────────────────────────────────────────
 Summary
 ─────────────────────────────────────────────────────────

 P0 (Critical): 2 tables with sensitive data exposed
 P1 (High): 2 tables with partial/unexpected exposure
 Blocked: 2 tables properly protected
 Expected: 2 tables with appropriate public access

 Total Rows Accessible: 1,892 across exposed tables

 Immediate Actions:
 1. Fix 'settings' table - remove from public or add RLS
 2. Fix 'users' table - add RLS to protect email/name
 3. Review 'comments' to hide user correlation

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

严重程度评估

状态严重程度标准
🔴 数据已暴露P0可访问敏感数据(PII、机密信息、财务信息)
🟠 部分访问P1可访问的数据超出预期,但不属于关键数据
🟡 出乎预期P2可访问,但数据风险较低
✅ 已阻止-RLS 正确阻止访问
✅ 符合预期-公开数据,访问权限适当

数据分类

此技能可识别以下敏感数据类型:

类型模式暴露时的严重程度
PIIemail、phone、name、addressP0
财务信息amount、total、card、paymentP0
机密信息key、secret、token、passwordP0
身份验证user_id、session、jwtP1
元数据created_at、updated_atP2

上下文输出

{
  "data_access": {
    "timestamp": "2025-01-31T10:30:00Z",
    "tables_tested": 8,
    "summary": {
      "p0_exposed": 2,
      "p1_partial": 2,
      "blocked": 2,
      "expected": 2
    },
    "results": [
      {
        "table": "users",
        "status": "exposed",
        "severity": "P0",
        "rows_accessible": 1247,
        "sensitive_columns": ["email", "name"],
        "sample_redacted": true
      },
      {
        "table": "settings",
        "status": "exposed",
        "severity": "P0",
        "rows_accessible": 3,
        "sensitive_data_types": ["secrets"],
        "finding": "Application secrets exposed"
      }
    ],
    "total_rows_accessible": 1892
  }
}

审计日志条目

[2025-01-31T10:30:00Z] READ_TEST_START tables=8
[2025-01-31T10:30:01Z] READ_TEST table=users status=200 rows=5 severity=P0
[2025-01-31T10:30:01Z] READ_TEST table=orders status=403 severity=none
[2025-01-31T10:30:02Z] READ_TEST_COMPLETE exposed=4 blocked=2

修复示例

用户表
-- Enable RLS
ALTER TABLE users ENABLE ROW LEVEL SECURITY;

-- Only authenticated users see their own data
CREATE POLICY "Users see own data"
  ON users FOR SELECT
  USING (auth.uid() = id);

-- Or create a public view with limited columns
CREATE VIEW public.users_public AS
  SELECT id, avatar_url, created_at FROM users;
设置表
-- Remove from public access entirely
REVOKE ALL ON TABLE settings FROM anon, authenticated;

-- Access only via Edge Functions
-- In your Edge Function:
const { data } = await supabaseAdmin
  .from('settings')
  .select('*')
  .eq('key', 'stripe_webhook_secret')
  .single()
内容表
-- RLS for published content only
CREATE POLICY "Public sees published posts"
  ON posts FOR SELECT
  USING (published = true);

-- Authors see their own drafts
CREATE POLICY "Authors see own posts"
  ON posts FOR SELECT
  USING (auth.uid() = author_id);

常见问题

❌ **问题:**所有表都返回 403
✅ **解决方案:**RLS 可能限制过严,或者 anon key 无效。从安全角度来看,这实际上是好事。

❌ **问题:**结果为空,但没有报错
✅ **解决方案:**RLS 正在筛除所有行。表结构已暴露,但没有数据。

❌ **问题:**大型表超时
✅ **解决方案:**使用计数模式或减少限制数量。

强制要求:渐进式更新上下文文件

⚠️ 此技能必须在执行过程中渐进式更新跟踪文件,而不是只在最后更新。

关键规则:边执行边写入

不要在最后批量写入。请改为:

  1. 测试每张表之前 → 将操作记录到 .sb-pentest-audit.log
  2. 每测试完一张表之后 → 立即将结果更新到 .sb-pentest-context.json
  3. 每发现一项问题之后 → 将严重程度记录到 .sb-pentest-audit.log

这样可以确保如果技能被中断、崩溃或超时,截至当时的所有发现都已保存。

必须执行的操作(渐进式)
  1. 更新 .sb-pentest-context.json,写入结果:

    {
      "data_access": {
        "timestamp": "...",
        "tables_tested": 8,
        "summary": { "p0_exposed": 2, ... },
        "results": [ ... ],
        "total_rows_accessible": 1892
      }
    }
    
  2. 记录到 .sb-pentest-audit.log:

    [TIMESTAMP] [supabase-audit-tables-read] [START] Testing data access
    [TIMESTAMP] [supabase-audit-tables-read] [FINDING] P0: users table exposed
    [TIMESTAMP] [supabase-audit-tables-read] [CONTEXT_UPDATED] .sb-pentest-context.json updated
    
  3. 如果文件不存在,请在写入前创建。

不更新上下文文件是不可接受的。

强制要求:收集证据

📁 证据目录: .sb-pentest-evidence/03-api-audit/data-samples/

要创建的证据文件
文件内容
data-samples/[table]-sample.json每个可访问表的示例数据
data-samples/[table]-blocked.json访问受阻的证明(403 响应)
证据格式(数据已暴露)
{
  "evidence_id": "API-READ-001",
  "timestamp": "2025-01-31T10:20:00Z",
  "category": "api-audit",
  "type": "data_access",
  "severity": "P0",
  "finding_id": "P0-002",

  "table": "users",

  "request": {
    "method": "GET",
    "url": "https://abc123def.supabase.co/rest/v1/users?select=*&limit=5",
    "headers": {
      "apikey": "[REDACTED]",
      "Authorization": "Bearer [REDACTED]"
    },
    "curl_command": "curl -s 'https://abc123def.supabase.co/rest/v1/users?select=*&limit=5' -H 'apikey: $ANON_KEY' -H 'Authorization: Bearer $ANON_KEY'"
  },

  "response": {
    "status": 200,
    "headers": {
      "content-range": "0-4/1247"
    },
    "total_rows": 1247,
    "sample_data": [
      {
        "id": "550e8400-e29b-41d4-...",
        "email": "[REDACTED]@example.com",
        "name": "[REDACTED]",
        "created_at": "2025-01-15T10:30:00Z"
      }
    ],
    "data_redacted": true
  },

  "analysis": {
    "severity": "P0",
    "pii_exposed": ["email", "name"],
    "total_records_accessible": 1247,
    "authentication_required": false
  }
}
证据格式(已正确阻止)
{
  "evidence_id": "API-READ-002",
  "timestamp": "2025-01-31T10:21:00Z",
  "table": "orders",
  "severity": null,

  "response": {
    "status": 403,
    "body": {"message": "new row violates row-level security policy"}
  },

  "analysis": {
    "rls_working": true,
    "access_blocked": true
  }
}
添加到 curl-commands.sh
# === DATA ACCESS TESTS ===
# Test: Users table access
curl -s "$SUPABASE_URL/rest/v1/users?select=*&limit=5" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ANON_KEY"

# Test: Orders table access (should be blocked)
curl -s "$SUPABASE_URL/rest/v1/orders?select=*&limit=5" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ANON_KEY"

相关技能

  • supabase-audit-tables-list — 首先列出表
  • supabase-audit-rls — 深入了解 RLS 策略
  • supabase-report — 生成完整报告

评论

支持 Markdown