supabase-extract-service-key

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Supabase Service Key Detection

Supabase服务密钥检测

🔴 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 detects if the service_role key (admin key) is accidentally exposed in client-side code.
🔴 严重警告:需要逐步更新文件
你必须在操作过程中写入上下文文件,而不是只在最后写入。
  • 每次发现问题后立即写入
    .sb-pentest-context.json
  • 每次操作前后都要记录到
    .sb-pentest-audit.log
  • 不要等到技能完成后才更新文件
  • 如果技能崩溃或被中断,所有已发现的结果必须已保存
这不是可选要求。不逐步写入文件属于严重错误。
本技能用于检测service_role密钥(管理员密钥)是否意外暴露在客户端代码中。

When to Use This Skill

何时使用本技能

  • As part of every security audit (this is critical)
  • When reviewing code before production deployment
  • After detecting Supabase usage to check for this common mistake
  • 作为每次安全审计的一部分(这一点至关重要)
  • 生产部署前的代码审查阶段
  • 检测到使用Supabase后,检查这一常见错误

Prerequisites

前置条件

  • Target application accessible
  • Supabase detection completed (auto-invokes if needed)
  • 可访问目标应用
  • 已完成Supabase检测(如果需要会自动调用)

Why This Is Critical

为何这一点至关重要

The service_role key bypasses ALL Row Level Security (RLS) policies. If exposed:
ImpactDescription
🔴 Full DB AccessRead/write/delete all data in all tables
🔴 Auth BypassAccess all user data without authentication
🔴 Storage AccessRead/write all files in all buckets
🔴 User ImpersonationGenerate tokens for any user
This is a P0 (Critical) finding that requires immediate action.
service_role密钥会绕过所有行级安全(RLS)策略。如果泄露:
影响描述
🔴 完全数据库访问权限可读写/删除所有表中的所有数据
🔴 身份验证绕过无需身份验证即可访问所有用户数据
🔴 存储访问权限可读写所有存储桶中的所有文件
🔴 用户模拟可为任意用户生成令牌
这是需要立即处理的P0(严重)级别问题。

Service Key vs Anon Key

服务密钥与匿名密钥对比

AspectAnon KeyService Key
Role claim
"role": "anon"
"role": "service_role"
RLS✅ Respects RLS❌ Bypasses RLS
Client-side✅ Expected❌ NEVER
Server-side✅ Can use✅ Should use
方面匿名密钥(Anon Key)服务密钥(Service Key)
角色声明
"role": "anon"
"role": "service_role"
RLS✅ 遵循RLS策略❌ 绕过RLS策略
客户端使用✅ 允许❌ 绝对禁止
服务端使用✅ 允许✅ 建议使用

Detection Patterns

检测模式

The skill searches for:
本技能会搜索以下内容:

1. Key with service_role Claim

1. 包含service_role声明的密钥

javascript
// Decoded JWT payload contains:
{
  "role": "service_role",  // ❌ CRITICAL if in client code
  "iss": "supabase",
  "ref": "abc123def"
}
javascript
// 解码后的JWT负载包含:
{
  "role": "service_role",  // ❌ 如果在客户端代码中则为严重问题
  "iss": "supabase",
  "ref": "abc123def"
}

2. Variable Names

2. 变量命名

javascript
// Common naming patterns
SUPABASE_SERVICE_KEY
SUPABASE_SERVICE_ROLE_KEY
SUPABASE_ADMIN_KEY
SUPABASE_SECRET_KEY
SERVICE_ROLE_KEY
javascript
// 常见命名模式
SUPABASE_SERVICE_KEY
SUPABASE_SERVICE_ROLE_KEY
SUPABASE_ADMIN_KEY
SUPABASE_SECRET_KEY
SERVICE_ROLE_KEY

3. Accidental Exposure

3. 意外暴露

javascript
// Sometimes exposed alongside anon key
const keys = {
  anon: 'eyJ...',
  service: 'eyJ...'  // ❌ Should not be here
}
javascript
// 有时会和匿名密钥一起暴露
const keys = {
  anon: 'eyJ...',
  service: 'eyJ...'  // ❌ 不应该出现在这里
}

Usage

使用方法

Basic Check

基础检查

Check for service key leak on https://myapp.example.com
检查https://myapp.example.com上的服务密钥泄露情况

Deep Scan

深度扫描

Deep scan for service key exposure on https://myapp.example.com
深度扫描https://myapp.example.com上的服务密钥暴露情况

Output Format

输出格式

No Service Key Found (Good)

未发现服务密钥(正常情况)

═══════════════════════════════════════════════════════════
 SERVICE KEY CHECK
═══════════════════════════════════════════════════════════

 Status: ✅ No service_role key detected in client code

 Scanned:
 ├── HTML source: Clean
 ├── JavaScript bundles: 5 files, 2.3MB analyzed
 ├── Inline scripts: 12 blocks checked
 └── Source maps: Not exposed (good)

 JWT Analysis:
 └── 1 key found, confirmed role=anon (safe)

 Result: PASS - No critical key exposure
═══════════════════════════════════════════════════════════
═══════════════════════════════════════════════════════════
 服务密钥检查
═══════════════════════════════════════════════════════════

 状态: ✅ 客户端代码中未检测到service_role密钥

 已扫描内容:
 ├── HTML源码: 无问题
 ├── JavaScript包: 5个文件,已分析2.3MB
 ├── 内联脚本: 检查了12个代码块
 └── 源映射: 未暴露(正常)

 JWT分析:
 └── 找到1个密钥,确认角色为anon(安全)

 结果: 通过 - 无严重密钥暴露问题
═══════════════════════════════════════════════════════════

Service Key FOUND (Critical)

发现服务密钥(严重情况)

═══════════════════════════════════════════════════════════
 🔴 CRITICAL: SERVICE KEY EXPOSED
═══════════════════════════════════════════════════════════

 Severity: P0 - CRITICAL
 Status: ❌ service_role key found in client-side code!

 ⚠️  IMMEDIATE ACTION REQUIRED ⚠️

 Exposed Key:
 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBh
 YmFzZSIsInJlZiI6ImFiYzEyM2RlZiIsInJvbGUiOiJzZXJ2aWNlX3
 JvbGUiLCJpYXQiOjE2NDAwMDAwMDAsImV4cCI6MTk1NTM2MDAwMH0
 .xxxxxxxxxxxxx

 Location:
 └── /static/js/admin.chunk.js (line 89)
     const SUPABASE_KEY = 'eyJhbG...'  // Used in createClient()

 Decoded Payload:
 ├── role: service_role ← CRITICAL
 ├── ref: abc123def
 └── exp: 2031-12-20

 Impact Assessment:
 ├── 🔴 Full database access possible
 ├── 🔴 All RLS policies bypassed
 ├── 🔴 All user data exposed
 └── 🔴 All storage buckets accessible

 ═══════════════════════════════════════════════════════════
 IMMEDIATE REMEDIATION STEPS
 ═══════════════════════════════════════════════════════════

 1. ROTATE THE KEY NOW
    → Supabase Dashboard > Settings > API > Regenerate service_role key

 2. REMOVE FROM CLIENT CODE
    → Delete the key from your source code
    → Redeploy your application

 3. AUDIT FOR ABUSE
    → Check Supabase logs for unauthorized access
    → Review database for unexpected changes

 4. USE EDGE FUNCTIONS
    → Move privileged operations to Edge Functions
    → Client calls Edge Function, which uses service key server-side

 Documentation:
 → https://supabase.com/docs/guides/api/api-keys
 → https://supabase.com/docs/guides/functions

═══════════════════════════════════════════════════════════
═══════════════════════════════════════════════════════════
 🔴 严重警告:服务密钥已暴露
═══════════════════════════════════════════════════════════

 严重级别: P0 - 严重
 状态: ❌ 客户端代码中发现service_role密钥!

 ⚠️  需要立即采取行动 ⚠️

 暴露的密钥:
 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBh
 YmFzZSIsInJlZiI6ImFiYzEyM2RlZiIsInJvbGUiOiJzZXJ2aWNlX3
 JvbGUiLCJpYXQiOjE2NDAwMDAwMDAsImV4cCI6MTk1NTM2MDAwMH0
 .xxxxxxxxxxxxx

 位置:
 └── /static/js/admin.chunk.js(第89行)
     const SUPABASE_KEY = 'eyJhbG...'  // 用于createClient()

 解码后的负载:
 ├── role: service_role ← 严重问题
 ├── ref: abc123def
 └── exp: 2031-12-20

 影响评估:
 ├── 🔴 可获取完整数据库访问权限
 ├── 🔴 所有RLS策略被绕过
 ├── 🔴 所有用户数据暴露
 └── 🔴 所有存储桶可访问

 ═══════════════════════════════════════════════════════════
 立即修复步骤
 ═══════════════════════════════════════════════════════════

 1. 立即轮换密钥
    → Supabase控制台 > 设置 > API > 重新生成service_role密钥

 2. 从客户端代码中移除
    → 从源码中删除该密钥
    → 重新部署应用

 3. 审计是否存在滥用情况
    → 检查Supabase日志中的未授权访问记录
    → 检查数据库是否有异常变更

 4. 使用Edge Functions
    → 将特权操作迁移到Edge Functions
    → 客户端调用Edge Function,由其在服务端使用服务密钥

 相关文档:
 → https://supabase.com/docs/guides/api/api-keys
 → https://supabase.com/docs/guides/functions

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

Context Output

上下文输出

Saved to
.sb-pentest-context.json
:
json
{
  "findings": [
    {
      "id": "SERVICE_KEY_EXPOSED",
      "severity": "P0",
      "title": "Service Role Key Exposed in Client Code",
      "description": "The service_role key was found in client-side JavaScript",
      "location": {
        "file": "/static/js/admin.chunk.js",
        "line": 89
      },
      "evidence": {
        "key_prefix": "eyJhbGciOiJIUzI1NiI...",
        "role": "service_role",
        "project_ref": "abc123def"
      },
      "remediation": {
        "immediate": "Rotate key in Supabase Dashboard",
        "long_term": "Move to Edge Functions",
        "docs": "https://supabase.com/docs/guides/api/api-keys"
      }
    }
  ],
  "supabase": {
    "service_key_exposed": true,
    "service_key_location": "/static/js/admin.chunk.js:89"
  }
}
保存到
.sb-pentest-context.json
:
json
{
  "findings": [
    {
      "id": "SERVICE_KEY_EXPOSED",
      "severity": "P0",
      "title": "Service Role Key Exposed in Client Code",
      "description": "The service_role key was found in client-side JavaScript",
      "location": {
        "file": "/static/js/admin.chunk.js",
        "line": 89
      },
      "evidence": {
        "key_prefix": "eyJhbGciOiJIUzI1NiI...",
        "role": "service_role",
        "project_ref": "abc123def"
      },
      "remediation": {
        "immediate": "Rotate key in Supabase Dashboard",
        "long_term": "Move to Edge Functions",
        "docs": "https://supabase.com/docs/guides/api/api-keys"
      }
    }
  ],
  "supabase": {
    "service_key_exposed": true,
    "service_key_location": "/static/js/admin.chunk.js:89"
  }
}

Source Maps Check

源映射检查

The skill also checks for exposed source maps that might reveal keys:
Source Maps Analysis:
├── main.js.map: ❌ Exposed (may contain secrets)
├── vendor.js.map: ❌ Exposed
└── Recommendation: Disable source maps in production

To check source maps content:
→ Add .map to JS URLs: /static/js/main.js.map
本技能还会检查是否存在可能泄露密钥的暴露源映射:
源映射分析:
├── main.js.map: ❌ 已暴露(可能包含敏感信息)
├── vendor.js.map: ❌ 已暴露
└── 建议: 生产环境中禁用源映射

检查源映射内容的方法:
→ 在JS URL后添加.map: /static/js/main.js.map

Common Causes

常见原因

CauseSolution
Wrong env variableUse
NEXT_PUBLIC_
only for anon key
Copy-paste errorDouble-check which key you're using
Debug code left inRemove before production build
Misconfigured bundlerEnsure service key env vars are not included
原因解决方案
环境变量使用错误仅对匿名密钥使用
NEXT_PUBLIC_
前缀
复制粘贴错误仔细检查所使用的密钥
遗留调试代码生产构建前移除调试代码
打包工具配置错误确保服务密钥的环境变量未被包含在打包内容中

Remediation Code Examples

修复代码示例

Before (Wrong)

修复前(错误写法)

javascript
// ❌ WRONG - Service key in client
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_SERVICE_KEY  // ❌ NEVER DO THIS
)
javascript
// ❌ 错误 - 客户端使用服务密钥
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_SERVICE_KEY  // ❌ 绝对不要这样做
)

After (Correct)

修复后(正确写法)

javascript
// ✅ CORRECT - Only anon key in client
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY  // ✅ Safe for client
)

// For privileged operations, call an Edge Function:
const { data } = await supabase.functions.invoke('admin-action', {
  body: { action: 'delete-user', userId: '123' }
})
javascript
// ✅ 正确 - 客户端仅使用匿名密钥
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY  // ✅ 客户端使用安全
)

// 对于特权操作,调用Edge Functions:
const { data } = await supabase.functions.invoke('admin-action', {
  body: { action: 'delete-user', userId: '123' }
})

Edge Function (Server-Side)

Edge Function(服务端)

typescript
// supabase/functions/admin-action/index.ts
import { createClient } from '@supabase/supabase-js'

Deno.serve(async (req) => {
  // ✅ Service key only on server
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL'),
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')  // ✅ Safe on server
  )

  // Perform privileged operation
  // ...
})
typescript
// supabase/functions/admin-action/index.ts
import { createClient } from '@supabase/supabase-js'

Deno.serve(async (req) => {
  // ✅ 仅在服务端使用服务密钥
  const supabase = createClient(
    Deno.env.get('SUPABASE_URL'),
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')  // ✅ 服务端使用安全
  )

  // 执行特权操作
  // ...
})

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.
不要将所有写入操作留到最后。而是:
  1. 开始任何操作前 → 将操作记录到
    .sb-pentest-audit.log
  2. 每次发现问题后 → 立即更新
    .sb-pentest-context.json
  3. 完成每个重要步骤后 → 将完成情况记录到
    .sb-pentest-audit.log
这样可以确保如果技能被中断、崩溃或超时,所有已发现的结果都会被保留。

Required Actions (Progressive)

必须执行的逐步操作

  1. Update
    .sb-pentest-context.json
    with findings:
    json
    {
      "supabase": {
        "service_key_exposed": true/false,
        "service_key_location": "path:line"
      },
      "findings": [
        {
          "id": "SERVICE_KEY_EXPOSED",
          "severity": "P0",
          "title": "Service Role Key Exposed",
          ...
        }
      ]
    }
  2. Log to
    .sb-pentest-audit.log
    :
    [TIMESTAMP] [supabase-extract-service-key] [START] Checking for service key exposure
    [TIMESTAMP] [supabase-extract-service-key] [CRITICAL] Service key EXPOSED at path:line
    [TIMESTAMP] [supabase-extract-service-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.
  1. 更新
    .sb-pentest-context.json
    ,写入发现的问题:
    json
    {
      "supabase": {
        "service_key_exposed": true/false,
        "service_key_location": "path:line"
      },
      "findings": [
        {
          "id": "SERVICE_KEY_EXPOSED",
          "severity": "P0",
          "title": "Service Role Key Exposed",
          ...
        }
      ]
    }
  2. 记录到
    .sb-pentest-audit.log
    :
    [TIMESTAMP] [supabase-extract-service-key] [START] Checking for service key exposure
    [TIMESTAMP] [supabase-extract-service-key] [CRITICAL] Service key EXPOSED at path:line
    [TIMESTAMP] [supabase-extract-service-key] [CONTEXT_UPDATED] .sb-pentest-context.json updated
  3. 如果文件不存在,在写入前创建文件。
不更新上下文文件是不被允许的。

MANDATORY: Evidence Collection

强制要求:证据收集

📁 Evidence Directory:
.sb-pentest-evidence/02-extraction/service-key-exposure/
📁 证据目录:
.sb-pentest-evidence/02-extraction/service-key-exposure/

Evidence Files to Create (if service key found)

需创建的证据文件(如果发现服务密钥)

FileContent
service-key-exposure/location.txt
File path and line number
service-key-exposure/decoded-payload.json
Decoded JWT proving it's service_role
service-key-exposure/code-snippet.txt
Code context (redacted)
文件内容
service-key-exposure/location.txt
文件路径和行号
service-key-exposure/decoded-payload.json
解码后的JWT,证明其为service_role密钥
service-key-exposure/code-snippet.txt
代码上下文(已脱敏)

Evidence Format (P0 Finding)

P0级问题的证据格式

json
{
  "evidence_id": "EXT-SVC-001",
  "timestamp": "2025-01-31T10:10:00Z",
  "category": "extraction",
  "type": "service_key_exposure",
  "severity": "P0",
  "finding_id": "P0-001",

  "key_data": {
    "key_prefix": "eyJhbGciOiJIUzI1NiI...",
    "key_suffix": "...xxxx",
    "role": "service_role"
  },

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

  "location": {
    "file": "/static/js/admin.chunk.js",
    "line": 89,
    "context": "const SUPABASE_KEY = 'eyJhbG...' // [REDACTED]"
  },

  "impact": {
    "rls_bypass": true,
    "full_db_access": true,
    "auth_users_access": true,
    "storage_access": true
  },

  "curl_command": "curl -X GET 'https://abc123def.supabase.co/rest/v1/users' -H 'apikey: [SERVICE_KEY]' -H 'Authorization: Bearer [SERVICE_KEY]'"
}
json
{
  "evidence_id": "EXT-SVC-001",
  "timestamp": "2025-01-31T10:10:00Z",
  "category": "extraction",
  "type": "service_key_exposure",
  "severity": "P0",
  "finding_id": "P0-001",

  "key_data": {
    "key_prefix": "eyJhbGciOiJIUzI1NiI...",
    "key_suffix": "...xxxx",
    "role": "service_role"
  },

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

  "location": {
    "file": "/static/js/admin.chunk.js",
    "line": 89,
    "context": "const SUPABASE_KEY = 'eyJhbG...' // [REDACTED]"
  },

  "impact": {
    "rls_bypass": true,
    "full_db_access": true,
    "auth_users_access": true,
    "storage_access": true
  },

  "curl_command": "curl -X GET 'https://abc123def.supabase.co/rest/v1/users' -H 'apikey: [SERVICE_KEY]' -H 'Authorization: Bearer [SERVICE_KEY]'"
}

Add to timeline.md (P0)

添加到timeline.md(P0级问题)

markdown
undefined
markdown
undefined

[TIMESTAMP] - 🔴 P0 CRITICAL: Service Role Key Exposed

[TIMESTAMP] - 🔴 P0 CRITICAL: Service Role Key Exposed

  • Service role key found in client-side code
  • Location: [file]:[line]
  • Impact: Full database access, RLS bypass
  • Evidence:
    02-extraction/service-key-exposure/
  • IMMEDIATE ACTION REQUIRED
undefined
  • Service role key found in client-side code
  • Location: [file]:[line]
  • Impact: Full database access, RLS bypass
  • Evidence:
    02-extraction/service-key-exposure/
  • IMMEDIATE ACTION REQUIRED
undefined

Related Skills

相关技能

  • supabase-extract-anon-key
    — Extract the (expected) anon key
  • supabase-audit-tables-read
    — Test what data is accessible
  • supabase-report
    — Generate full report including this finding
  • supabase-extract-anon-key
    — 提取(预期的)匿名密钥
  • supabase-audit-tables-read
    — 测试可访问的数据内容
  • supabase-report
    — 生成包含该问题的完整报告