重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
supabase-extract-jwt by yoanbernabeu/supabase-pentest-skills
npx skills add https://github.com/yoanbernabeu/supabase-pentest-skills --skill supabase-extract-jwt🔴 关键:需要渐进式文件更新
你必须在操作过程中就写入上下文文件,而不是等到最后才写。
- 每次发现后立即写入
.sb-pentest-context.json- 每次操作前后记录到
.sb-pentest-audit.log- 不要等到技能完成才更新文件
- 如果技能崩溃或被中断,所有之前的发现必须已经保存
这不是可选的。未能渐进式写入是一个严重错误。
此技能从客户端代码中提取和分析与 Supabase 相关的 JSON Web 令牌。
| 类型 | 用途 |
|---|
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 客户端暴露情况 |
|---|
| 匿名密钥 | API 身份验证 | ✅ 预期 |
| 服务角色密钥 | 管理员访问 | ❌ 绝不 |
| 访问令牌 | 用户会话 | ⚠️ 仅动态 |
| 刷新令牌 | 令牌续订 | ⚠️ 仅动态 |
// Supabase API keys are JWTs
const SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
// ❌ Should never be hardcoded
const userToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZW1haWwiOiJ1c2VyQGV4YW1wbGUuY29tIn0...'
// Code referencing where JWTs are stored
localStorage.getItem('supabase.auth.token')
localStorage.getItem('sb-abc123-auth-token')
sessionStorage.getItem('supabase_session')
Extract JWTs from https://myapp.example.com
Extract and analyze all JWTs from https://myapp.example.com
═══════════════════════════════════════════════════════════
JWT 提取结果
═══════════════════════════════════════════════════════════
发现:3 个 JWT
─────────────────────────────────────────────────────────
JWT #1: Supabase 匿名密钥
─────────────────────────────────────────────────────────
类型:API 密钥(匿名)
状态:✅ 预期在客户端代码中
头部:
├── alg: HS256
└── typ: JWT
载荷:
├── iss: supabase
├── ref: abc123def
├── role: anon
├── iat: 2021-12-20T00:00:00Z
└── exp: 2031-12-20T00:00:00Z
位置:/static/js/main.js:1247
─────────────────────────────────────────────────────────
JWT #2: 硬编码的用户令牌 ⚠️
─────────────────────────────────────────────────────────
类型:用户访问令牌
状态:⚠️ P1 - 不应硬编码
头部:
├── alg: HS256
└── typ: JWT
载荷:
├── sub: 12345678-1234-1234-1234-123456789012
├── email: developer@company.com
├── role: authenticated
├── iat: 2025-01-15T10:00:00Z
└── exp: 2025-01-15T11:00:00Z (已过期)
位置:/static/js/debug.js:45
风险:此令牌可能属于真实的用户账户。
即使已过期,它也暴露了用户信息。
─────────────────────────────────────────────────────────
JWT #3: 存储引用
─────────────────────────────────────────────────────────
类型:存储密钥模式
状态:ℹ️ 信息性
模式:localStorage.getItem('sb-abc123def-auth-token')
位置:/static/js/auth.js:89
注意:这是用户会话的预期存储密钥。
实际的令牌值在运行时设置。
═══════════════════════════════════════════════════════════
此技能识别关键声明:
| 声明 | 描述 | 安全影响 |
|---|---|---|
sub | 用户 ID | 标识特定用户 |
email | 用户邮箱 | 如果硬编码则暴露 PII |
role | 权限级别 | service_role 是关键 |
exp | 过期时间 | 过期的令牌风险较低 |
iat | 签发时间 | 指示创建时间 |
| 声明 | 描述 |
|---|---|
ref | 项目引用 |
iss | 应为 "supabase" |
aal | 身份验证器保证级别 |
amr | 使用的身份验证方法 |
🔴 Service role key exposed (role: service_role)
→ Immediate key rotation required
🟠 User token hardcoded with PII (email, sub visible)
→ Remove from code, may need to notify user
🟡 Expired test token in code
→ Clean up, potential information disclosure
保存到 .sb-pentest-context.json:
{
"jwts": {
"found": 3,
"api_keys": [
{
"type": "anon",
"project_ref": "abc123def",
"location": "/static/js/main.js:1247"
}
],
"user_tokens": [
{
"type": "access_token",
"hardcoded": true,
"severity": "P1",
"claims": {
"sub": "12345678-1234-1234-1234-123456789012",
"email": "developer@company.com",
"expired": true
},
"location": "/static/js/debug.js:45"
}
],
"storage_patterns": [
{
"pattern": "sb-abc123def-auth-token",
"storage": "localStorage",
"location": "/static/js/auth.js:89"
}
]
}
}
❌ 问题: JWT 看起来被截断了 ✅ 解决方案: 可能跨越多行。技能会尝试重新组装。
❌ 问题: JWT 无法解码 ✅ 解决方案: 可能已加密(JWE)或是自定义格式。标记为不可解码。
❌ 问题: 许多误报 ✅ 解决方案: 看起来像 JWT 的 Base64 字符串。技能会验证结构。
// ❌ Never hardcode user tokens
const adminToken = 'eyJhbGciOiJIUzI1NiI...'
fetch('/api/admin', {
headers: { Authorization: `Bearer ${adminToken}` }
})
// ✅ Get token from Supabase session
const { data: { session } } = await supabase.auth.getSession()
fetch('/api/admin', {
headers: { Authorization: `Bearer ${session.access_token}` }
})
⚠️ 此技能必须在执行过程中渐进式地更新跟踪文件,而不是仅在最后更新。
不要在最后批量写入所有内容。而是:
.sb-pentest-audit.log.sb-pentest-context.json.sb-pentest-audit.log这确保了如果技能被中断、崩溃或超时,所有到该点为止的发现都会被保留。
更新 .sb-pentest-context.json 并包含提取的数据:
{ "jwts": { "found": 3, "api_keys": [ ... ], "user_tokens": [ ... ], "storage_patterns": [ ... ] } }
记录到 .sb-pentest-audit.log:
[TIMESTAMP] [supabase-extract-jwt] [START] Beginning JWT extraction [TIMESTAMP] [supabase-extract-jwt] [SUCCESS] Found 3 JWTs [TIMESTAMP] [supabase-extract-jwt] [CONTEXT_UPDATED] .sb-pentest-context.json updated
如果文件不存在,在写入前创建它们。
未能更新上下文文件是不可接受的。
📁 证据目录: .sb-pentest-evidence/02-extraction/
| 文件 | 内容 |
|---|---|
extracted-jwts.json | 发现的所有 JWT 及其分析 |
{
"evidence_id": "EXT-JWT-001",
"timestamp": "2025-01-31T10:08:00Z",
"category": "extraction",
"type": "jwt_extraction",
"jwts_found": [
{
"type": "anon_key",
"severity": "info",
"location": "/static/js/main.js:1247",
"decoded_payload": {
"iss": "supabase",
"ref": "abc123def",
"role": "anon"
}
},
{
"type": "hardcoded_user_token",
"severity": "P1",
"location": "/static/js/debug.js:45",
"decoded_payload": {
"sub": "[REDACTED]",
"email": "[REDACTED]@example.com",
"role": "authenticated",
"exp": "2025-01-15T11:00:00Z"
},
"expired": true,
"issue": "Hardcoded user token with PII"
}
],
"storage_patterns_found": [
{
"pattern": "localStorage.getItem('sb-abc123def-auth-token')",
"location": "/static/js/auth.js:89"
}
]
}
supabase-extract-anon-key — 专门提取匿名密钥supabase-extract-service-key — 检查服务密钥(关键)supabase-audit-auth-config — 分析身份验证配置每周安装量
92
仓库
GitHub 星标数
33
首次出现
Jan 31, 2026
安全审计
安装于
claude-code76
codex68
opencode67
gemini-cli64
github-copilot59
cursor59
🔴 CRITICAL: PROGRESSIVE FILE UPDATES REQUIRED
You MUST write to context files AS YOU GO , not just at the end.
- Write to
.sb-pentest-context.jsonIMMEDIATELY after each discovery- Log to
.sb-pentest-audit.logBEFORE 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 and analyzes JSON Web Tokens (JWTs) related to Supabase from client-side code.
| Type | Purpose | Client Exposure |
|---|---|---|
| Anon Key | API authentication | ✅ Expected |
| Service Role Key | Admin access | ❌ Never |
| Access Token | User session | ⚠️ Dynamic only |
| Refresh Token | Token renewal | ⚠️ Dynamic only |
// Supabase API keys are JWTs
const SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
// ❌ Should never be hardcoded
const userToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZW1haWwiOiJ1c2VyQGV4YW1wbGUuY29tIn0...'
// Code referencing where JWTs are stored
localStorage.getItem('supabase.auth.token')
localStorage.getItem('sb-abc123-auth-token')
sessionStorage.getItem('supabase_session')
Extract JWTs from https://myapp.example.com
Extract and analyze all JWTs from https://myapp.example.com
═══════════════════════════════════════════════════════════
JWT EXTRACTION RESULTS
═══════════════════════════════════════════════════════════
Found: 3 JWTs
─────────────────────────────────────────────────────────
JWT #1: Supabase Anon Key
─────────────────────────────────────────────────────────
Type: API Key (anon)
Status: ✅ Expected in client code
Header:
├── alg: HS256
└── typ: JWT
Payload:
├── iss: supabase
├── ref: abc123def
├── role: anon
├── iat: 2021-12-20T00:00:00Z
└── exp: 2031-12-20T00:00:00Z
Location: /static/js/main.js:1247
─────────────────────────────────────────────────────────
JWT #2: Hardcoded User Token ⚠️
─────────────────────────────────────────────────────────
Type: User Access Token
Status: ⚠️ P1 - Should not be hardcoded
Header:
├── alg: HS256
└── typ: JWT
Payload:
├── sub: 12345678-1234-1234-1234-123456789012
├── email: developer@company.com
├── role: authenticated
├── iat: 2025-01-15T10:00:00Z
└── exp: 2025-01-15T11:00:00Z (EXPIRED)
Location: /static/js/debug.js:45
Risk: This token may belong to a real user account.
Even if expired, it reveals user information.
─────────────────────────────────────────────────────────
JWT #3: Storage Reference
─────────────────────────────────────────────────────────
Type: Storage Key Pattern
Status: ℹ️ Informational
Pattern: localStorage.getItem('sb-abc123def-auth-token')
Location: /static/js/auth.js:89
Note: This is the expected storage key for user sessions.
Actual token value is set at runtime.
═══════════════════════════════════════════════════════════
The skill identifies key claims:
| Claim | Description | Security Impact |
|---|---|---|
sub | User ID | Identifies specific user |
email | User email | PII exposure if hardcoded |
role | Permission level | service_role is critical |
exp | Expiration | Expired tokens less risky |
iat |
| Claim | Description |
|---|---|
ref | Project reference |
iss | Should be "supabase" |
aal | Authenticator assurance level |
amr | Authentication methods used |
🔴 Service role key exposed (role: service_role)
→ Immediate key rotation required
🟠 User token hardcoded with PII (email, sub visible)
→ Remove from code, may need to notify user
🟡 Expired test token in code
→ Clean up, potential information disclosure
Saved to .sb-pentest-context.json:
{
"jwts": {
"found": 3,
"api_keys": [
{
"type": "anon",
"project_ref": "abc123def",
"location": "/static/js/main.js:1247"
}
],
"user_tokens": [
{
"type": "access_token",
"hardcoded": true,
"severity": "P1",
"claims": {
"sub": "12345678-1234-1234-1234-123456789012",
"email": "developer@company.com",
"expired": true
},
"location": "/static/js/debug.js:45"
}
],
"storage_patterns": [
{
"pattern": "sb-abc123def-auth-token",
"storage": "localStorage",
"location": "/static/js/auth.js:89"
}
]
}
}
❌ Problem: JWT appears truncated ✅ Solution: May span multiple lines. The skill attempts to reassemble.
❌ Problem: JWT won't decode ✅ Solution: May be encrypted (JWE) or custom format. Noted as undecodable.
❌ Problem: Many false positives ✅ Solution: Base64 strings that look like JWTs. Skill validates structure.
// ❌ Never hardcode user tokens
const adminToken = 'eyJhbGciOiJIUzI1NiI...'
fetch('/api/admin', {
headers: { Authorization: `Bearer ${adminToken}` }
})
// ✅ Get token from Supabase session
const { data: { session } } = await supabase.auth.getSession()
fetch('/api/admin', {
headers: { Authorization: `Bearer ${session.access_token}` }
})
⚠️ This skill MUST update tracking files PROGRESSIVELY during execution, NOT just at the end.
DO NOT batch all writes at the end. Instead:
.sb-pentest-audit.log.sb-pentest-context.json.sb-pentest-audit.logThis ensures that if the skill is interrupted, crashes, or times out, all findings up to that point are preserved.
Update.sb-pentest-context.json with extracted data:
{ "jwts": { "found": 3, "api_keys": [ ... ], "user_tokens": [ ... ], "storage_patterns": [ ... ] } }
Log to.sb-pentest-audit.log:
[TIMESTAMP] [supabase-extract-jwt] [START] Beginning JWT extraction [TIMESTAMP] [supabase-extract-jwt] [SUCCESS] Found 3 JWTs [TIMESTAMP] [supabase-extract-jwt] [CONTEXT_UPDATED] .sb-pentest-context.json updated
If files don't exist , create them before writing.
FAILURE TO UPDATE CONTEXT FILES IS NOT ACCEPTABLE.
📁 Evidence Directory: .sb-pentest-evidence/02-extraction/
| File | Content |
|---|---|
extracted-jwts.json | All JWTs found with analysis |
{
"evidence_id": "EXT-JWT-001",
"timestamp": "2025-01-31T10:08:00Z",
"category": "extraction",
"type": "jwt_extraction",
"jwts_found": [
{
"type": "anon_key",
"severity": "info",
"location": "/static/js/main.js:1247",
"decoded_payload": {
"iss": "supabase",
"ref": "abc123def",
"role": "anon"
}
},
{
"type": "hardcoded_user_token",
"severity": "P1",
"location": "/static/js/debug.js:45",
"decoded_payload": {
"sub": "[REDACTED]",
"email": "[REDACTED]@example.com",
"role": "authenticated",
"exp": "2025-01-15T11:00:00Z"
},
"expired": true,
"issue": "Hardcoded user token with PII"
}
],
"storage_patterns_found": [
{
"pattern": "localStorage.getItem('sb-abc123def-auth-token')",
"location": "/static/js/auth.js:89"
}
]
}
supabase-extract-anon-key — Specifically extracts the anon keysupabase-extract-service-key — Checks for service key (critical)supabase-audit-auth-config — Analyzes auth configurationWeekly Installs
92
Repository
GitHub Stars
33
First Seen
Jan 31, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykFail
Installed on
claude-code76
codex68
opencode67
gemini-cli64
github-copilot59
cursor59
通过 LiteLLM 代理让 Claude Code 对接 GitHub Copilot 运行 | 高级变通方案指南
48,700 周安装
| Issued at |
| Indicates when created |