mcpField
S
supabase-extract-service-key

supabase-extract-service-key

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

关于 supabase-extract-service-key

supabase-extract-service-key 是安全审计技能,用于检查客户端代码中是否暴露 Supabase service_role 密钥。它检查密钥特征和源映射文件,并记录审计发现与证据。

主要功能

  1. 01检测客户端代码中的 service_role 密钥
  2. 02检查常见服务密钥变量名
  3. 03检查可访问的源映射文件
  4. 04记录发现与支持证据

适用场景

  1. 01审计 Web 应用中的服务密钥泄露
  2. 02部署前检查客户端构建文件
  3. 03调查源映射文件是否泄露密钥

安装

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

$npx skills add https://github.com/yoanbernabeu/supabase-pentest-skills --skill supabase-extract-service-key

安装后在 Claude Code 中输入/supabase-extract-service-key确认已加载。

详细介绍

Supabase 服务密钥检测

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

必须边执行边写入上下文文件,而不是只在最后写入。

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

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

此技能用于检测 service_role 密钥(管理员密钥)是否意外暴露在客户端代码中。

何时使用此技能

  • 作为每次安全审计的一部分(这一点至关重要)
  • 在生产部署前审查代码时
  • 检测到使用 Supabase 后,检查这一常见错误

前提条件

  • 可访问目标应用
  • 已完成 Supabase 检测(如果需要,会自动调用)

此项为何至关重要

service_role 密钥会绕过所有行级安全性(RLS)策略。如果密钥泄露:

影响说明
🔴 完全访问数据库读取、写入、删除所有表中的所有数据
🔴 绕过身份验证无需身份验证即可访问所有用户数据
🔴 访问存储读取、写入所有存储桶中的所有文件
🔴 冒充用户为任意用户生成令牌

这是一个需要立即处理的 P0(严重)发现。

Service 密钥与 Anon 密钥

方面Anon 密钥Service 密钥
角色声明"role": "anon""role": "service_role"
RLS✅ 遵守 RLS❌ 绕过 RLS
客户端✅ 符合预期❌ 绝不允许
服务端✅ 可以使用✅ 应当使用

检测模式

此技能会搜索:

1. 带有 service_role 声明的密钥
// Decoded JWT payload contains:
{
  "role": "service_role",  // ❌ CRITICAL if in client code
  "iss": "supabase",
  "ref": "abc123def"
}
2. 变量名
// Common naming patterns
SUPABASE_SERVICE_KEY
SUPABASE_SERVICE_ROLE_KEY
SUPABASE_ADMIN_KEY
SUPABASE_SECRET_KEY
SERVICE_ROLE_KEY
3. 意外暴露
// Sometimes exposed alongside anon key
const keys = {
  anon: 'eyJ...',
  service: 'eyJ...'  // ❌ Should not be here
}

用法

基本检查
Check for service key leak on https://myapp.example.com
深度扫描
Deep scan for service key exposure on https://myapp.example.com

输出格式

未发现 Service 密钥(良好)
═══════════════════════════════════════════════════════════
 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 密钥(严重)
═══════════════════════════════════════════════════════════
 🔴 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

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

上下文输出

保存到 .sb-pentest-context.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 Map

此技能还会检查是否暴露了可能泄露密钥的 source map:

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

常见原因

原因解决方案
环境变量错误仅对 anon 密钥使用 NEXT_PUBLIC_
复制粘贴错误仔细确认所用的密钥
遗留调试代码在生产构建前移除
打包工具配置错误确保未包含 service 密钥环境变量

修复代码示例

修复前(错误)
// ❌ 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
)
修复后(正确)
// ✅ 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' }
})
Edge Function(服务端)
// 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
  // ...
})

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

⚠️ 此技能执行期间必须渐进式更新跟踪文件,而不能只在最后更新。

关键规则:边执行边写入

不要在最后批量写入。应当:

  1. 开始任何操作之前 → 将操作记录到 .sb-pentest-audit.log
  2. 每次发现之后 → 立即更新 .sb-pentest-context.json
  3. 每个重要步骤之后 → 将完成情况记录到 .sb-pentest-audit.log

这样可以确保即使技能被中断、崩溃或超时,截至当时的所有发现都已保存。

必须执行的操作(渐进式)
  1. 使用发现内容更新 .sb-pentest-context.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. 如果文件不存在,则在写入前创建文件。

未更新上下文文件不可接受。

强制要求:收集证据

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

要创建的证据文件(如果发现 service 密钥)
文件内容
service-key-exposure/location.txt文件路径和行号
service-key-exposure/decoded-payload.json解码后的 JWT,用以证明其为 service_role
service-key-exposure/code-snippet.txt代码上下文(已编辑敏感信息)
证据格式(P0 发现)
{
  "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]'"
}
添加到 timeline.md(P0)
## [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**

相关技能

  • supabase-extract-anon-key — 提取(预期的)anon key
  • supabase-audit-tables-read — 测试哪些数据可访问
  • supabase-report — 生成包含此发现的完整报告

评论

支持 Markdown