Supabase认证用户审计技能Skill supabase-audit-authenticated

此技能用于在Supabase中创建测试用户,进行安全审计,以检测认证用户与匿名用户的访问差异,识别IDOR漏洞、跨用户数据访问和权限提升问题。关键词:Supabase安全审计、认证用户测试、IDOR检测、网络安全、数据库安全。

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

名称: supabase-audit-authenticated 描述: 创建一个测试用户(具有明确权限)来审计认证用户可以访问的内容与匿名用户的对比。检测IDOR、跨用户访问和权限提升。

认证用户审计

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

您必须随着进展写入上下文文件,而不仅仅是在最后。

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

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

此技能创建一个测试用户(具有明确权限)来比较认证与匿名访问,并检测IDOR漏洞。

⚠️ 重要:需要用户同意

╔═══════════════════════════════════════════════════════════════════╗
║  🔐 需要用户创建同意                                              ║
╠═══════════════════════════════════════════════════════════════════╣
║                                                                   ║
║  此技能将在您的Supabase项目中创建一个测试用户。                    ║
║                                                                   ║
║  用户将使用以下信息创建:                                         ║
║  • 邮箱:pentest-[随机]@security-audit.local                     ║
║  • 密码:强随机密码(32+字符)                                    ║
║  • 目的:测试认证访问与匿名访问                                    ║
║                                                                   ║
║  审计结束时,您将被询问是否要删除测试用户(推荐)。                ║
║                                                                   ║
║  您是否授权创建测试用户?                                         ║
║  输入“yes, create test user”以继续。                               ║
║                                                                   ║
╚═══════════════════════════════════════════════════════════════════╝

没有明确用户同意,请不要继续。

何时使用此技能

  • 完成匿名访问测试后
  • 检测IDOR(不安全直接对象引用)漏洞
  • 测试跨用户数据访问
  • 验证RLS策略对认证用户有效
  • 查找权限提升问题

先决条件

  • 注册必须开放(或使用邀请流程)
  • 匿名密钥可用
  • 匿名审计已完成(推荐)

为什么认证测试重要

许多漏洞仅在认证时出现:

漏洞 匿名 认证
RLS绕过(无RLS) ✓ 可检测 ✓ 可检测
IDOR ✗ 不可见 仅可见
跨用户访问 ✗ 不可见 仅可见
权限提升 ✗ 不可见 仅可见
过于宽松的RLS 部分 完全检测

测试用户创建

邮箱格式

pentest-[8字符随机]@security-audit.local

示例:pentest-a7b3c9d2@security-audit.local

密码生成

强密码要求:

  • 32+ 字符
  • 大写、小写、数字、符号
  • 加密随机

示例:Xk9$mP2#vL5@nQ8&jR4*wY7!hT3%bU6^

密码仅显示一次并保存到证据。

执行的测试

1. 用户创建和登录

# 创建用户
curl -X POST "$SUPABASE_URL/auth/v1/signup" \
  -H "apikey: $ANON_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "pentest-xxx@security-audit.local", "password": "[STRONG_PASSWORD]"}'

# 登录并获取JWT
curl -X POST "$SUPABASE_URL/auth/v1/token?grant_type=password" \
  -H "apikey: $ANON_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "pentest-xxx@security-audit.local", "password": "[STRONG_PASSWORD]"}'

2. 认证与匿名比较

对于每个表:

测试 匿名 认证 发现
SELECT 0行 1,247行 🔴 仅认证暴露
自有数据 N/A 仅自有行 ✅ RLS工作正常
其他用户数据 N/A 所有行 🔴 跨用户访问

3. IDOR测试

# 作为测试用户,尝试访问其他用户的数据
curl "$SUPABASE_URL/rest/v1/orders?user_id=eq.[OTHER_USER_ID]" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer [TEST_USER_JWT]"

# 如果返回数据:IDOR漏洞!

4. 跨用户访问

# 从JWT获取测试用户ID
TEST_USER_ID=$(echo $JWT | jq -r '.sub')

# 尝试访问属于不同用户的数据
curl "$SUPABASE_URL/rest/v1/profiles?id=neq.$TEST_USER_ID" \
  -H "Authorization: Bearer [TEST_USER_JWT]"

# 如果返回其他用户个人资料:跨用户访问!

5. 带认证的存储

# 测试认证存储访问
curl "$SUPABASE_URL/storage/v1/object/list/documents" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer [TEST_USER_JWT]"

# 与匿名结果比较

6. 带认证的实时

// 作为认证用户订阅表变化
const channel = supabase.channel('test')
  .on('postgres_changes', {
    event: '*',
    schema: 'public',
    table: 'orders'
  }, payload => console.log(payload))
  .subscribe()

// 它是否接收到其他用户的订单变化?

输出格式

═══════════════════════════════════════════════════════════
 认证用户审计
═══════════════════════════════════════════════════════════

 ─────────────────────────────────────────────────────────
 测试用户创建
 ─────────────────────────────────────────────────────────

 状态:✅ 用户创建成功

 测试用户详情:
 ├── 邮箱:pentest-a7b3c9d2@security-audit.local
 ├── 用户ID:550e8400-e29b-41d4-a716-446655440099
 ├── 密码:[保存到证据 - 仅显示一次]
 └── JWT获取:✅

 ─────────────────────────────────────────────────────────
 匿名与认证比较
 ─────────────────────────────────────────────────────────

 表:users
 ├── 匿名访问:0行
 ├── 认证访问:1,247行 ← 所有用户!
 └── 状态:🔴 P0 - 数据对匿名隐藏但暴露给任何认证用户

 表:orders
 ├── 匿名访问:0行(阻止)
 ├── 认证访问:1行(仅自有订单)
 └── 状态:✅ RLS工作正确

 表:profiles
 ├── 匿名访问:0行
 ├── 认证访问:1,247行 ← 所有个人资料!
 ├── 仅期望自有个人资料:❌ 否
 └── 状态:🔴 P0 - 跨用户个人资料访问

 ─────────────────────────────────────────────────────────
 IDOR测试
 ─────────────────────────────────────────────────────────

 测试:通过ID访问其他用户的订单
 ├── 请求:GET /orders?user_id=eq.[other-user-id]
 ├── 认证:测试用户JWT
 ├── 响应:200 OK - 15个订单返回
 └── 状态:🔴 P0 - IDOR漏洞

 证明:
 curl "$URL/rest/v1/orders?user_id=eq.other-user-uuid" \
   -H "Authorization: Bearer [test-user-jwt]"
 # 返回属于other-user-uuid的订单!

 测试:访问管理端点
 ├── 请求:GET /functions/v1/admin-panel
 ├── 认证:测试用户JWT(普通用户)
 ├── 响应:200 OK - 管理员数据返回!
 └── 状态:🔴 P0 - 权限提升

 ─────────────────────────────────────────────────────────
 带认证的存储
 ─────────────────────────────────────────────────────────

 存储桶:documents
 ├── 匿名:❌ 0个文件(阻止)
 ├── 认证:✅ 523个文件可见 ← 所有用户的文件!
 └── 状态:🔴 P1 - 认证用户看到所有文档

 存储桶:user-uploads
 ├── 匿名:❌ 0个文件
 ├── 认证:3个文件(仅自有文件)
 └── 状态:✅ RLS工作正确

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

 新发现(仅认证):
 ├── 🔴 P0:users表 - 所有用户对任何认证用户可见
 ├── 🔴 P0:profiles表 - 跨用户访问
 ├── 🔴 P0:orders中的IDOR - 可以访问任何用户的订单
 ├── 🔴 P0:admin-panel中的权限提升
 └── 🟠 P1:documents存储桶 - 所有文件对认证用户可见

 比较:
 ├── 发现的问题(匿名):3
 ├── 发现的问题(认证):8 ← 5个新问题!
 └── 仅认证漏洞:5

 建议:
 这些问题在匿名测试中不可见!
 始终使用认证用户进行测试。

 ─────────────────────────────────────────────────────────
 清理
 ─────────────────────────────────────────────────────────

 ⚠️  测试用户仍在数据库中。

 您要删除测试用户吗?
 邮箱:pentest-a7b3c9d2@security-audit.local

 [这需要服务角色密钥或手动删除]

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

上下文输出

{
  "authenticated_audit": {
    "timestamp": "2025-01-31T12:00:00Z",
    "test_user": {
      "email": "pentest-a7b3c9d2@security-audit.local",
      "user_id": "550e8400-e29b-41d4-a716-446655440099",
      "created_at": "2025-01-31T12:00:00Z",
      "deleted": false
    },
    "comparison": {
      "tables": {
        "users": {
          "anon_access": 0,
          "auth_access": 1247,
          "expected_auth_access": "own_row_only",
          "severity": "P0",
          "finding": "所有用户对任何认证用户可见"
        },
        "orders": {
          "anon_access": 0,
          "auth_access": 1,
          "expected_auth_access": "own_rows_only",
          "severity": null,
          "finding": "RLS工作正确"
        }
      },
      "idor_tests": [
        {
          "test": "access_other_user_orders",
          "vulnerable": true,
          "severity": "P0",
          "proof": "curl命令..."
        }
      ],
      "privilege_escalation": [
        {
          "endpoint": "/functions/v1/admin-panel",
          "vulnerable": true,
          "severity": "P0"
        }
      ]
    },
    "summary": {
      "anon_issues": 3,
      "auth_issues": 8,
      "auth_only_issues": 5
    }
  }
}

RLS策略示例

正确:用户仅看到自有数据

-- 此RLS策略正确
CREATE POLICY "用户看到自有数据"
  ON users FOR SELECT
  USING (auth.uid() = id);

-- 结果:
-- 匿名:0行
-- 认证:1行(自有数据)

错误:所有认证用户看到所有内容

-- 此RLS策略错误
CREATE POLICY "认证用户看到所有"
  ON users FOR SELECT
  USING (auth.role() = 'authenticated');  -- ❌ 过于宽松!

-- 结果:
-- 匿名:0行
-- 认证:所有行 ← 漏洞!

正确修复:

-- 修复:添加用户所有权检查
CREATE POLICY "用户看到自有数据"
  ON users FOR SELECT
  USING (auth.uid() = id);  -- ✅ 仅自有行

清理选项

选项1:手动删除(仪表板)

Supabase仪表板 → 认证 → 用户 → 找到测试用户 → 删除

选项2:通过服务角色密钥(如果可用)

curl -X DELETE "$SUPABASE_URL/auth/v1/admin/users/[USER_ID]" \
  -H "apikey: $SERVICE_ROLE_KEY" \
  -H "Authorization: Bearer $SERVICE_ROLE_KEY"

选项3:留待后续处理

测试用户使用非功能性邮箱域(security-audit.local),无法被恶意使用。

必需:证据收集

📁 证据目录: .sb-pentest-evidence/05-auth-audit/authenticated-tests/

要创建的证据文件

文件 内容
test-user-created.json 测试用户详情(密码安全保存)
anon-vs-auth-comparison.json 并排比较
idor-tests/[table].json IDOR测试结果
privilege-escalation.json 权限提升测试

证据格式

{
  "evidence_id": "AUTH-TEST-001",
  "timestamp": "2025-01-31T12:00:00Z",
  "category": "auth-audit",
  "type": "authenticated_testing",

  "test_user": {
    "email": "pentest-a7b3c9d2@security-audit.local",
    "user_id": "550e8400-...",
    "password": "[安全存储 - 不要提交]"
  },

  "comparison_test": {
    "table": "users",
    "anonymous": {
      "curl_command": "curl '$URL/rest/v1/users' -H 'apikey: $ANON_KEY'",
      "response_status": 200,
      "rows_returned": 0
    },
    "authenticated": {
      "curl_command": "curl '$URL/rest/v1/users' -H 'apikey: $ANON_KEY' -H 'Authorization: Bearer $JWT'",
      "response_status": 200,
      "rows_returned": 1247
    },
    "finding": {
      "severity": "P0",
      "issue": "所有用户对任何认证用户可见",
      "expected": "仅应看到自有行",
      "impact": "任何认证用户可进行完整用户枚举"
    }
  }
}

添加到curl-commands.sh

# === 认证测试 ===
# 注意:用测试用户的JWT替换[JWT]

# 比较匿名与认证访问
curl -s "$SUPABASE_URL/rest/v1/users?select=*&limit=5" -H "apikey: $ANON_KEY"
curl -s "$SUPABASE_URL/rest/v1/users?select=*&limit=5" -H "apikey: $ANON_KEY" -H "Authorization: Bearer [JWT]"

# IDOR测试 - 访问其他用户的数据
curl -s "$SUPABASE_URL/rest/v1/orders?user_id=eq.[OTHER_USER_ID]" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer [JWT]"

# 跨用户个人资料访问
curl -s "$SUPABASE_URL/rest/v1/profiles?id=neq.[TEST_USER_ID]" \
  -H "apikey: $ANON_KEY" \
  -H "Authorization: Bearer [JWT]"

必需:渐进式上下文文件更新

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

关键规则:随进展写入

不要在最后批量所有写入。而是:

  1. 在用户创建前 → 将同意和操作记录到 .sb-pentest-audit.log
  2. 在用户创建后 → 立即将用户详情保存到上下文和证据
  3. 在每次比较测试后 → 更新 .sb-pentest-context.json 与结果
  4. 在每次IDOR测试后 → 立即保存证据

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

必需操作(渐进式)

  1. 记录用户创建:

    [时间戳] [supabase-audit-authenticated] [同意] 用户授权测试用户创建
    [时间戳] [supabase-audit-authenticated] [创建] 测试用户 pentest-xxx@security-audit.local
    
  2. 立即保存测试用户到上下文:

    {
      "authenticated_audit": {
        "test_user": {
          "email": "...",
          "user_id": "...",
          "created_at": "..."
        }
      }
    }
    
  3. 记录每个发现时:

    [时间戳] [supabase-audit-authenticated] [发现] P0:orders表中的IDOR
    

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

相关技能

  • supabase-audit-auth-signup — 首先测试注册是否开放
  • supabase-audit-tables-read — 与匿名结果比较
  • supabase-audit-rls — 深入探讨RLS策略
  • supabase-audit-functions — 使用认证测试函数访问
  • supabase-report — 在报告中包含仅认证发现