mcpField
S
supabase-audit-auth-users

supabase-audit-auth-users

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

关于 supabase-audit-auth-users

用于 Supabase 认证安全审计的技能,通过检查注册、登录、恢复和 OTP 流程,识别用户枚举及响应时间泄漏,并逐步保存审计结果。

主要功能

  1. 01检查注册、登录、恢复和 OTP 端点
  2. 02比较错误信息、状态码与响应时间
  3. 03渐进更新上下文和审计日志
  4. 04保存测试证据并提供缓解建议

适用场景

  1. 01上线前检查认证流程是否泄露用户存在信息
  2. 02审计密码恢复和 OTP 请求的响应差异

安装

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

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

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

详细介绍

用户枚举审计

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

你必须在操作过程中写入上下文文件,而不是等到最后才写。

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

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

此技能用于测试身份验证流程中的用户枚举漏洞。

何时使用此技能

  • 检查是否可以检测到用户是否存在
  • 测试登录、注册和账户恢复流程是否泄露信息
  • 作为身份验证安全审计的一部分
  • 在部署到生产环境之前

前提条件

  • 已获取 Supabase URL 和 anon key
  • 身份验证端点可访问

什么是用户枚举?

当应用通过以下方式泄露用户账户是否存在时,就会发生用户枚举:

途径指示信号
不同的错误消息“未找到用户”与“密码错误”
响应时间不存在的账户响应快,已存在的账户响应慢
响应代码404 与 401
注册响应“电子邮件已注册”

重要性

风险影响
定向攻击攻击者知道哪些账户有效
网络钓鱼确认目标是否拥有账户
凭证填充攻击缩小攻击范围
隐私泄露用户是否存在

执行的测试

端点测试方法
/auth/v1/signup尝试注册已存在的电子邮件
/auth/v1/token尝试使用各种电子邮件登录
/auth/v1/recover尝试重置密码
/auth/v1/otp尝试为各种电子邮件获取 OTP

用法

基本枚举测试
Test for user enumeration vulnerabilities
测试特定端点
Test login endpoint for user enumeration

输出格式

═══════════════════════════════════════════════════════════
 USER ENUMERATION AUDIT
═══════════════════════════════════════════════════════════

 Project: abc123def.supabase.co

 ─────────────────────────────────────────────────────────
 Signup Endpoint (/auth/v1/signup)
 ─────────────────────────────────────────────────────────

 Test: POST with known existing email
 Response for existing: "User already registered"
 Response for new email: User object returned

 Status: 🟠 P2 - ENUMERABLE

 The response clearly indicates if an email is registered.

 Exploitation:
 ```bash
 curl -X POST https://abc123def.supabase.co/auth/v1/signup \
   -H "apikey: [anon-key]" \
   -H "Content-Type: application/json" \
   -d '{"email": "[email protected]", "password": "test123"}'

 # If user exists: {"msg": "User already registered"}
 # If new user: User created or confirmation needed

───────────────────────────────────────────────────────── 登录端点(/auth/v1/token) ─────────────────────────────────────────────────────────

测试:使用不同的电子邮件场景发送 POST 请求

电子邮件已存在,密码错误: ├── 响应:{"error": "Invalid login credentials"} ├── 耗时:245ms └── 状态码:400

电子邮件不存在: ├── 响应:{"error": "Invalid login credentials"} ├── 耗时:52ms ← 快得多! └── 状态码:400

状态:🟠 P2 - 可通过计时枚举

虽然错误消息相同,但响应时间有明显差异: ├── 现有用户:~200-300ms(密码哈希处理) └── 不存在的用户:~50-100ms(不进行哈希检查)

计时攻击概念验证:

import requests
import time

def check_user(email):
    start = time.time()
    requests.post(
        'https://abc123def.supabase.co/auth/v1/token',
        params={'grant_type': 'password'},
        json={'email': email, 'password': 'wrong'},
        headers={'apikey': '[anon-key]'}
    )
    elapsed = time.time() - start
    return elapsed > 0.15  # Threshold

exists = check_user('[email protected]')

───────────────────────────────────────────────────────── 密码恢复(/auth/v1/recover) ─────────────────────────────────────────────────────────

测试:使用不同的电子邮件发送 POST 恢复请求

电子邮件已存在: ├── 响应:{"message": "Password recovery email sent"} ├── 耗时:1250ms(实际发送了电子邮件) └── 状态码:200

电子邮件不存在: ├── 响应:{"message": "Password recovery email sent"} ├── 耗时:85ms ← 快得多(未发送电子邮件) └── 状态码:200

状态:🟠 P2 - 可通过计时枚举

消息相同,但计时信息会暴露用户是否存在。 现有用户会触发实际的电子邮件发送(约 1 秒以上)。

───────────────────────────────────────────────────────── Magic Link / OTP(/auth/v1/otp) ─────────────────────────────────────────────────────────

测试:为不同的电子邮件请求 OTP

电子邮件已存在: ├── 响应:{"message": "OTP sent"} ├── 耗时:1180ms └── 状态码:200

电子邮件不存在: ├── 响应:{"error": "User not found"} ├── 耗时:95ms └── 状态码:400

状态:🔴 P1 - 可直接枚举

错误消息明确说明用户不存在。

───────────────────────────────────────────────────────── 总结 ─────────────────────────────────────────────────────────

已测试的端点:4 可枚举:4(100%)

漏洞严重程度: ├── 🔴 P1:OTP 端点(消息明确) ├── 🟠 P2:注册端点(消息明确) ├── 🟠 P2:登录端点(计时攻击) └── 🟠 P2:恢复端点(计时攻击)

整体用户枚举风险:高

攻击者可以判断任何电子邮件地址 是否在你的应用中拥有账户。

───────────────────────────────────────────────────────── 缓解建议 ─────────────────────────────────────────────────────────

  1. 统一响应 在所有场景中返回相同的消息: “如果账户存在,你将收到一封电子邮件”

  2. 统一耗时 添加人为延迟,使响应时间趋于一致:

    const MIN_RESPONSE_TIME = 1000; // 1 second
    const start = Date.now();
    // ... perform auth operation ...
    const elapsed = Date.now() - start;
    await new Promise(r => setTimeout(r,
      Math.max(0, MIN_RESPONSE_TIME - elapsed)
    ));
    return response;
    
  3. 速率限制 已启用:每个 IP 每小时 3 次 也可考虑按电子邮件进行速率限制。

  4. CAPTCHA 对重复尝试添加 CAPTCHA:

    • 3 次登录失败后
    • 密码恢复时
    • 注册时
  5. 监控 对枚举模式发出警报:

    • 使用不同电子邮件发起大量请求
    • 连续的电子邮件模式(user1@、user2@……)

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


## Timing Analysis

The skill measures response times to detect timing-based enumeration:

现有用户: ├── 密码哈希验证:约 200-300ms ├── 发送电子邮件:约 1000-2000ms └── 数据库查询:约 5-20ms

不存在的用户: ├── 无哈希验证:0ms ├── 不发送电子邮件:0ms └── 数据库查询:约 5-20ms(未找到)


Threshold detection:
- Difference > 100ms: Possible timing leak
- Difference > 500ms: Definite timing leak

## Context Output

```json
{
  "user_enumeration": {
    "timestamp": "2025-01-31T13:30:00Z",
    "endpoints_tested": 4,
    "vulnerabilities": [
      {
        "endpoint": "/auth/v1/otp",
        "severity": "P1",
        "type": "explicit_message",
        "existing_response": "OTP sent",
        "missing_response": "User not found"
      },
      {
        "endpoint": "/auth/v1/signup",
        "severity": "P2",
        "type": "explicit_message",
        "existing_response": "User already registered",
        "missing_response": "User created"
      },
      {
        "endpoint": "/auth/v1/token",
        "severity": "P2",
        "type": "timing_attack",
        "existing_time_ms": 245,
        "missing_time_ms": 52
      },
      {
        "endpoint": "/auth/v1/recover",
        "severity": "P2",
        "type": "timing_attack",
        "existing_time_ms": 1250,
        "missing_time_ms": 85
      }
    ]
  }
}

缓解措施代码示例

统一响应时间
// Edge Function with normalized timing
const MIN_RESPONSE_TIME = 1500; // 1.5 seconds

Deno.serve(async (req) => {
  const start = Date.now();

  try {
    // Perform actual auth operation
    const result = await handleAuth(req);

    // Normalize response time
    const elapsed = Date.now() - start;
    await new Promise(r => setTimeout(r,
      Math.max(0, MIN_RESPONSE_TIME - elapsed)
    ));

    return new Response(JSON.stringify(result));
  } catch (error) {
    // Same timing for errors
    const elapsed = Date.now() - start;
    await new Promise(r => setTimeout(r,
      Math.max(0, MIN_RESPONSE_TIME - elapsed)
    ));

    // Generic error message
    return new Response(JSON.stringify({
      message: "Check your email if you have an account"
    }));
  }
});
通用错误消息
// Don't reveal user existence
async function requestPasswordReset(email: string) {
  // Always return success message
  const response = {
    message: "If an account with that email exists, " +
             "you will receive a password reset link."
  };

  // Perform actual reset in background (don't await)
  supabase.auth.resetPasswordForEmail(email).catch(() => {});

  return response;
}

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

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

关键规则:边做边写

不要等到最后才批量写入。请按以下步骤操作:

  1. 测试每个端点之前 → 将操作记录到 .sb-pentest-audit.log
  2. 每次测量时间之后 → 立即更新 .sb-pentest-context.json
  3. 每发现一个枚举向量之后 → 立即记录发现

这样可以确保即使技能执行中断、崩溃或超时,截至当时的所有发现都能得到保留。

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

    {
      "user_enumeration": {
        "timestamp": "...",
        "endpoints_tested": 4,
        "vulnerabilities": [ ... ]
      }
    }
    
  2. 记录到 .sb-pentest-audit.log:

    [TIMESTAMP] [supabase-audit-auth-users] [START] Testing user enumeration
    [TIMESTAMP] [supabase-audit-auth-users] [FINDING] P1: OTP endpoint enumerable
    [TIMESTAMP] [supabase-audit-auth-users] [CONTEXT_UPDATED] .sb-pentest-context.json updated
    
  3. 如果文件不存在,请在写入前创建。

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

必须:收集证据

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

需要创建的证据文件
文件内容
enumeration-tests/login-timing.json登录端点时间分析
enumeration-tests/recovery-timing.json恢复端点时间分析
enumeration-tests/otp-enumeration.jsonOTP 端点消息分析
证据格式
{
  "evidence_id": "AUTH-ENUM-001",
  "timestamp": "2025-01-31T11:00:00Z",
  "category": "auth-audit",
  "type": "user_enumeration",

  "tests": [
    {
      "endpoint": "/auth/v1/token",
      "test_type": "timing_attack",
      "severity": "P2",

      "existing_user_test": {
        "email": "[KNOWN_EXISTING]@example.com",
        "response_time_ms": 245,
        "response": {"error": "Invalid login credentials"}
      },

      "nonexisting_user_test": {
        "email": "[email protected]",
        "response_time_ms": 52,
        "response": {"error": "Invalid login credentials"}
      },

      "timing_difference_ms": 193,
      "result": "ENUMERABLE",
      "impact": "Can determine if email has account via timing"
    },
    {
      "endpoint": "/auth/v1/otp",
      "test_type": "explicit_message",
      "severity": "P1",

      "existing_user_response": {"message": "OTP sent"},
      "nonexisting_user_response": {"error": "User not found"},

      "result": "ENUMERABLE",
      "impact": "Error message explicitly reveals user existence"
    }
  ],

  "curl_commands": [
    "# Timing test - existing user\ntime curl -X POST '$URL/auth/v1/token?grant_type=password' -H 'apikey: $ANON_KEY' -d '{\"email\": \"[email protected]\", \"password\": \"wrong\"}'",
    "# Timing test - non-existing user\ntime curl -X POST '$URL/auth/v1/token?grant_type=password' -H 'apikey: $ANON_KEY' -d '{\"email\": \"[email protected]\", \"password\": \"wrong\"}'"
  ]
}

相关技能

  • supabase-audit-auth-config — 完整的身份验证配置
  • supabase-audit-auth-signup — 注册流程测试
  • supabase-report — 纳入最终报告

评论

支持 Markdown