home / skills / yoanbernabeu / supabase-pentest-skills / supabase-extract-anon-key

supabase-extract-anon-key skill

/skills/extraction/supabase-extract-anon-key

This skill extracts the Supabase anon key from client code to enable secure testing and RLS verification across projects.

npx playbooks add skill yoanbernabeu/supabase-pentest-skills --skill supabase-extract-anon-key

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
9.0 KB
---
name: supabase-extract-anon-key
description: Extract the Supabase anon/public API key from client-side code. This key is expected in client apps but important for RLS testing.
---

# Supabase Anon Key Extraction

> πŸ”΄ **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 discovery**
> - Log to `.sb-pentest-audit.log` **BEFORE and AFTER each action**
> - **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 extracts the Supabase anonymous (public) API key from client-side code.

## When to Use This Skill

- After extracting the Supabase URL, to get the API key for testing
- To verify that only the anon key (not service key) is exposed
- Before running API audit skills that require authentication

## Prerequisites

- Supabase URL extracted (or will auto-invoke `supabase-extract-url`)
- Target application accessible

## Understanding Anon Keys

The **anon key** (also called public key) is:

- βœ… **Expected** to be in client-side code
- βœ… **Safe** when RLS (Row Level Security) is properly configured
- ⚠️ **Risky** if RLS is missing or misconfigured
- ❌ **Not the same** as the service_role key (which should NEVER be in client code)

### Key Format

Supabase anon keys are JWTs:

```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFiYzEyMyIsInJvbGUiOiJhbm9uIiwiaWF0IjoxNjQwMDAwMDAwLCJleHAiOjE5NTUzNjAwMDB9.xxxx
```

Key characteristics:
- Starts with `eyJ` (base64 encoded `{"alg":`)
- Contains `"role":"anon"` in payload
- Project reference in `"ref"` claim

## Extraction Patterns

The skill searches for:

### 1. Direct Key Assignment

```javascript
const SUPABASE_KEY = 'eyJhbGci...'
const SUPABASE_ANON_KEY = 'eyJhbGci...'
```

### 2. Client Initialization

```javascript
createClient(url, 'eyJhbGci...')
createClient(url, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY)
```

### 3. Environment Variable Patterns

```javascript
NEXT_PUBLIC_SUPABASE_ANON_KEY
VITE_SUPABASE_ANON_KEY
REACT_APP_SUPABASE_KEY
SUPABASE_KEY
```

## Usage

### Basic Extraction

```
Extract Supabase anon key from https://myapp.example.com
```

### If URL Already Known

```
Extract anon key for project abc123def
```

## Output Format

```
═══════════════════════════════════════════════════════════
 ANON KEY EXTRACTED
═══════════════════════════════════════════════════════════

 Key Type: anon (public)
 Severity: ℹ️  Expected (verify RLS configuration)

 Key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJz
      dXBhYmFzZSIsInJlZiI6ImFiYzEyM2RlZiIsInJvbGUiOiJhbm
      9uIiwiaWF0IjoxNjQwMDAwMDAwLCJleHAiOjE5NTUzNjAwMDB9
      .xxxxxxxxxxxxx

 Decoded Payload:
 β”œβ”€β”€ iss: supabase
 β”œβ”€β”€ ref: abc123def
 β”œβ”€β”€ role: anon
 β”œβ”€β”€ iat: 2021-12-20T00:00:00Z
 └── exp: 2031-12-20T00:00:00Z

 Found in:
 └── /static/js/main.js (line 1253)
     createClient('https://abc123def.supabase.co', 'eyJhbGci...')

 Next Steps:
 β”œβ”€β”€ Run supabase-audit-rls to test if RLS protects your data
 β”œβ”€β”€ Run supabase-audit-tables-read to see what's accessible
 └── Run supabase-extract-service-key to check for critical leaks

 Context updated: .sb-pentest-context.json
═══════════════════════════════════════════════════════════
```

## Key Validation

The skill validates the extracted key:

```
Validation:
β”œβ”€β”€ Format: βœ… Valid JWT structure
β”œβ”€β”€ Decode: βœ… Payload readable
β”œβ”€β”€ Role: βœ… Confirmed "anon" role
β”œβ”€β”€ Project: βœ… Matches extracted URL (abc123def)
└── Expiry: βœ… Not expired (expires 2031-12-20)
```

## Multiple Keys

If multiple keys are found:

```
═══════════════════════════════════════════════════════════
 MULTIPLE KEYS FOUND
═══════════════════════════════════════════════════════════

 ⚠️  2 potential Supabase keys detected

 1. Anon Key (confirmed)
    └── Role: anon, Project: abc123def

 2. Unknown Key
    └── Role: service_role ⚠️  SEE supabase-extract-service-key
        This may be a CRITICAL security issue!

═══════════════════════════════════════════════════════════
```

## Context Output

Saved to `.sb-pentest-context.json`:

```json
{
  "supabase": {
    "anon_key": "eyJhbGci...",
    "anon_key_decoded": {
      "iss": "supabase",
      "ref": "abc123def",
      "role": "anon",
      "iat": 1640000000,
      "exp": 1955360000
    },
    "anon_key_sources": [
      {
        "file": "/static/js/main.js",
        "line": 1253
      }
    ]
  }
}
```

## Security Assessment

| Finding | Severity | Description |
|---------|----------|-------------|
| Anon key in client | ℹ️ Info | Expected, but test RLS |
| Anon key expired | ⚠️ P2 | Key should be rotated |
| Multiple anon keys | ⚠️ P2 | May indicate key rotation issues |
| Role is not "anon" | πŸ”΄ P0 | Wrong key type exposed! |

## Common Issues

❌ **Problem:** Key found but won't decode
βœ… **Solution:** May be obfuscated or split. Try:
```
Extract anon key with deobfuscation from https://myapp.example.com
```

❌ **Problem:** Key doesn't match URL project
βœ… **Solution:** App may use multiple Supabase projects. Both keys are recorded.

❌ **Problem:** No key found but Supabase detected
βœ… **Solution:** Key may be fetched at runtime. Check network requests:
```
Monitor network for anon key on https://myapp.example.com
```

## Best Practices Reminder

For developers reading this report:

1. **Anon key in client is normal** β€” It's designed for this
2. **RLS is critical** β€” The anon key relies on RLS for security
3. **Never use service_role in client** β€” Use Edge Functions instead
4. **Rotate keys periodically** β€” Available in Supabase Dashboard

## MANDATORY: Progressive Context File Updates

⚠️ **This skill MUST update tracking files PROGRESSIVELY during execution, NOT just at the end.**

### Critical Rule: Write As You Go

**DO NOT** batch all writes at the end. Instead:

1. **Before starting any action** β†’ Log the action to `.sb-pentest-audit.log`
2. **After each discovery** β†’ Immediately update `.sb-pentest-context.json`
3. **After each significant step** β†’ Log completion to `.sb-pentest-audit.log`

This ensures that if the skill is interrupted, crashes, or times out, all findings up to that point are preserved.

### Required Actions (Progressive)

1. **Update `.sb-pentest-context.json`** with extracted data:
   ```json
   {
     "supabase": {
       "anon_key": "eyJhbGci...",
       "anon_key_decoded": { ... },
       "anon_key_sources": [ ... ]
     }
   }
   ```

2. **Log to `.sb-pentest-audit.log`**:
   ```
   [TIMESTAMP] [supabase-extract-anon-key] [START] Beginning anon key extraction
   [TIMESTAMP] [supabase-extract-anon-key] [SUCCESS] Anon key extracted
   [TIMESTAMP] [supabase-extract-anon-key] [CONTEXT_UPDATED] .sb-pentest-context.json updated
   ```

3. **If files don't exist**, create them before writing.

**FAILURE TO UPDATE CONTEXT FILES IS NOT ACCEPTABLE.**

## MANDATORY: Evidence Collection

πŸ“ **Evidence Directory:** `.sb-pentest-evidence/02-extraction/`

### Evidence Files to Create

| File | Content |
|------|---------|
| `extracted-anon-key.json` | Anon key with decoded JWT payload |

### Evidence Format

```json
{
  "evidence_id": "EXT-ANON-001",
  "timestamp": "2025-01-31T10:07:00Z",
  "category": "extraction",
  "type": "anon_key",
  "severity": "info",

  "key_data": {
    "key_prefix": "eyJhbGciOiJIUzI1NiI...",
    "key_suffix": "...xxxx",
    "full_key_length": 256
  },

  "decoded_payload": {
    "iss": "supabase",
    "ref": "abc123def",
    "role": "anon",
    "iat": "2021-12-20T00:00:00Z",
    "exp": "2031-12-20T00:00:00Z"
  },

  "source": {
    "file": "/static/js/main.js",
    "line": 1253,
    "context": "createClient('https://abc123def.supabase.co', 'eyJhbGci...')"
  },

  "validation": {
    "format_valid": true,
    "role_confirmed": "anon",
    "project_matches": true,
    "expired": false
  }
}
```

## Related Skills

- `supabase-extract-url` β€” Get URL first (auto-invoked if needed)
- `supabase-extract-service-key` β€” Check for critical service key leak
- `supabase-audit-rls` β€” Test if RLS protects your data
- `supabase-audit-tables-read` β€” See what data is accessible with this key

Overview

This skill extracts the Supabase anon (public) API key from client-side code and validates its format and claims. It records discovery context and evidence immediately so findings are preserved during interruption. Use the result to drive RLS testing and further API audits.

How this skill works

The skill scans client assets and build artifacts for common key patterns, createClient calls, and environment variable usages. It validates JWT structure, decodes the payload to confirm the role and project ref, and matches the key to any known Supabase URL. Every discovery is logged and saved progressively to context and evidence files.

When to use it

  • After locating a Supabase project URL to obtain the public key for testing
  • Before running RLS or table-read audits that need an anon key
  • When verifying that only anon (not service_role) keys appear in client code
  • When keys may be obfuscated, split across lines, or provided via env variables
  • When network-fetched keys need confirmation of origin and claims

Best practices

  • Record findings progressively: log actions and update context immediately after each discovery
  • Validate key structure and decode payload to confirm role=anon and matching project ref
  • Treat anon keys as expected but rely on RLS for actual security testing
  • Flag and escalate any non-anon (service_role) keys found in client code
  • Collect structured evidence (timestamped JSON) and include file/line context for reproducibility

Example use cases

  • Extract anon key from https://myapp.example.com to prepare for RLS tests
  • Scan static/js bundles to find createClient(url, 'eyJ...') calls and decode the token
  • Search for env var patterns (NEXT_PUBLIC_SUPABASE_ANON_KEY, VITE_SUPABASE_ANON_KEY) in built artifacts
  • Confirm that a discovered JWT has role=anon and matches the project ref from the Supabase URL
  • Produce a small evidence JSON with decoded payload and source file/line for audit reports

FAQ

Is an anon key dangerous if found in client code?

Noβ€”anon keys are expected in client apps, but they rely on properly configured RLS; test RLS to assess actual risk.

What if the key won't decode?

It may be obfuscated or split. Try deobfuscation and reassembly, or monitor network requests at runtime.

What if multiple keys are found?

Record each key, decode payloads, and flag any non-anon roles (service_role) as critical.