Supabase数据访问审计技能Skill supabase-audit-tables-read

这个技能用于审计Supabase数据库中的表数据访问,测试行级安全(RLS)有效性,识别数据暴露风险,并生成安全报告。关键词包括:Supabase审计、数据安全、RLS测试、渗透测试、安全审计。

安全审计 0 次安装 0 次浏览 更新于 3/18/2026

name: supabase-audit-tables-read description: 尝试从暴露的表中读取数据,以验证实际数据暴露情况和RLS有效性。

表数据访问测试

🔴 关键:需要渐进式文件更新

您必须逐步写入上下文文件,而不是仅在最后。

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

这是不可选的。未能渐进式写入是关键错误。

这个技能尝试从暴露的表中读取数据,以确定实际上可以访问哪些信息。

何时使用这个技能

  • 列出表后,以验证实际访问
  • 测试RLS策略有效性
  • 评估数据暴露的严重性
  • 记录确切可以检索的数据

先决条件

  • 已列出表(如果需要,自动调用supabase-audit-tables-list
  • 匿名密钥可用

工作原理

该技能在每个暴露的表上执行SELECT查询:

GET https://[项目].supabase.co/rest/v1/[表]?select=*&limit=5
Authorization: Bearer [匿名密钥]

重要: 这是只读的。不会修改或删除任何数据。

测试模式

模式 描述 查询
快速 每个表的前5行 ?limit=5
样本 跨表的随机样本 ?limit=10&order=random
计数 仅行计数,无数据 HEAD请求

用法

基本读取测试

测试暴露表的读取访问

仅快速计数

计数所有表中可访问的行(无数据检索)

特定表

测试用户表的读取访问

输出格式

═══════════════════════════════════════════════════════════
 数据访问测试结果
═══════════════════════════════════════════════════════════

 测试模式:快速(每个表5行)
 测试表数:8

 ─────────────────────────────────────────────────────────
 按表结果
 ─────────────────────────────────────────────────────────

 1. users
    状态:🔴 P0 - 数据暴露
    检索行数:5(共1,247行)
    样本数据:
    ┌─────────────────────────────────────────────────────┐
    │ id: 550e8400-e29b-41d4-a716-446655440001           │
    │ email: john.doe@example.com ← PII暴露               │
    │ name: John Doe ← PII暴露                            │
    │ avatar_url: https://...                            │
    │ created_at: 2025-01-15T10:30:00Z                   │
    └─────────────────────────────────────────────────────┘
    发现:用户邮箱和名称无需认证即可访问

 2. profiles
    状态:🟠 P1 - 部分访问
    检索行数:5
    备注:仅返回公共字段(RLS部分工作)
    可见列:id, bio, website
    被阻止列:user_id, social_links, private_notes

 3. posts
    状态:✅ 预期访问
    检索行数:5
    备注:仅返回published=true的帖子(RLS工作)
    数据:公共内容,访问级别适当

 4. orders
    状态:✅ 被阻止
    响应:403 Forbidden
    消息:"new row violates row-level security policy"
    备注:RLS正确阻止访问

 5. api_keys
    状态:✅ 被阻止
    响应:403 Forbidden
    备注:RLS正确保护秘密

 6. products
    状态:✅ 预期访问
    检索行数:5
    备注:公共目录数据,访问适当

 7. comments
    状态:🟠 P1 - 比预期更多数据
    检索行数:5
    问题:user_id列暴露(可关联到用户)
    建议:使用视图隐藏user_id

 8. settings
    状态:🔴 P0 - 敏感数据暴露
    检索行数:3
    样本数据:
    ┌─────────────────────────────────────────────────────┐
    │ key: stripe_webhook_secret                          │
    │ value: whsec_xxxxxxxxxxxx ← 秘密暴露                │
    └─────────────────────────────────────────────────────┘
    发现:应用程序秘密在可访问表中!

 ─────────────────────────────────────────────────────────
 摘要
 ─────────────────────────────────────────────────────────

 P0(关键):2个表有敏感数据暴露
 P1(高):2个表有部分/意外暴露
 被阻止:2个表正确保护
 预期:2个表有适当公共访问

 可访问总行数:1,892个暴露表

 立即行动:
 1. 修复'settings'表 - 从公开移除或添加RLS
 2. 修复'users'表 - 添加RLS以保护邮箱/名称
 3. 审查'comments'以隐藏用户关联

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

严重性评估

状态 严重性 标准
🔴 数据暴露 P0 敏感数据(PII、秘密、财务)可访问
🟠 部分访问 P1 比预期更多数据,但不关键
🟡 意外 P2 可访问但低风险数据
✅ 被阻止 - RLS正确防止访问
✅ 预期 - 公共数据,访问适当

数据分类

该技能识别敏感数据类型:

类型 模式 暴露严重性
PII email, phone, name, address P0
财务 amount, total, card, payment P0
秘密 key, secret, token, password P0
认证 user_id, session, jwt P1
元数据 created_at, updated_at P2

上下文输出

{
  "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": "应用程序秘密暴露"
      }
    ],
    "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

修复示例

对于用户表

-- 启用RLS
ALTER TABLE users ENABLE ROW LEVEL SECURITY;

-- 仅认证用户看到自己的数据
CREATE POLICY "用户看到自己的数据"
  ON users FOR SELECT
  USING (auth.uid() = id);

-- 或创建具有有限列的公共视图
CREATE VIEW public.users_public AS
  SELECT id, avatar_url, created_at FROM users;

对于设置表

-- 完全从公共访问移除
REVOKE ALL ON TABLE settings FROM anon, authenticated;

-- 仅通过边缘函数访问
-- 在边缘函数中:
const { data } = await supabaseAdmin
  .from('settings')
  .select('*')
  .eq('key', 'stripe_webhook_secret')
  .single()

对于内容表

-- RLS仅用于已发布内容
CREATE POLICY "公众看到已发布的帖子"
  ON posts FOR SELECT
  USING (published = true);

-- 作者看到自己的草稿
CREATE POLICY "作者看到自己的帖子"
  ON posts FOR SELECT
  USING (auth.uid() = author_id);

常见问题

问题: 所有表返回403 ✅ 解决方案: RLS可能太严格或匿名密钥无效。从安全角度看,这实际上是好的。

问题: 空结果但无错误 ✅ 解决方案: 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] 测试数据访问
    [TIMESTAMP] [supabase-audit-tables-read] [FINDING] P0: users表暴露
    [TIMESTAMP] [supabase-audit-tables-read] [CONTEXT_UPDATED] .sb-pentest-context.json更新
    
  3. 如果文件不存在,在写入前创建它们。

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

强制:证据收集

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

要创建的证据文件

文件 内容
data-samples/[表]-sample.json 从每个可访问表的样本数据
data-samples/[表]-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

# === 数据访问测试 ===
# 测试:用户表访问
curl -s "$SUPABASE_URL/rest/v1/users?select=*&limit=5" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer $ANON_KEY"

# 测试:订单表访问(应被阻止)
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 — 生成完整报告