名称: supabase-extract-jwt 描述: 从客户端代码、cookies和本地存储模式中提取和解码Supabase相关的JSON Web Tokens(JWT)。
Supabase JWT 提取
🔴 关键:需要渐进式文件更新
您必须逐步写入上下文文件,而不是仅在最后写入。
- 每次发现后立即写入
.sb-pentest-context.json- 每次操作前后记录到
.sb-pentest-audit.log- 不要等到技能完成才更新文件
- 如果技能崩溃或中断,所有之前的发现必须已经保存
这不是可选的。未能逐步写入是关键错误。
此技能从客户端代码中提取和分析与Supabase相关的JSON Web Tokens(JWT)。
何时使用此技能
- 查找客户端代码中暴露的所有JWT令牌
- 分析令牌声明和过期时间
- 检测硬编码的用户令牌(安全问题)
- 理解认证流程
先决条件
- 目标应用程序可访问
- Supabase检测已完成(如果需要,自动调用)
Supabase中的JWT类型
| 类型 | 目的 | 客户端暴露 |
|---|---|---|
| 匿名密钥 | API认证 | ✅ 预期 |
| 服务角色密钥 | 管理员访问 | ❌ 从不 |
| 访问令牌 | 用户会话 | ⚠️ 仅动态 |
| 刷新令牌 | 令牌更新 | ⚠️ 仅动态 |
检测模式
1. API密钥(静态)
// Supabase API密钥是JWT
const SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
2. 硬编码用户令牌(问题)
// ❌ 永远不应硬编码
const userToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZW1haWwiOiJ1c2VyQGV4YW1wbGUuY29tIn0...'
3. 存储密钥模式
// 引用JWT存储位置的代码
localStorage.getItem('supabase.auth.token')
localStorage.getItem('sb-abc123-auth-token')
sessionStorage.getItem('supabase_session')
使用
基本提取
从 https://myapp.example.com 提取JWT
带声明分析
从 https://myapp.example.com 提取和分析所有JWT
输出格式
═══════════════════════════════════════════════════════════
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
注意: 这是用户会话的预期存储密钥。
实际令牌值在运行时设置。
═══════════════════════════════════════════════════════════
JWT 声明分析
此技能识别关键声明:
标准声明
| 声明 | 描述 | 安全影响 |
|---|---|---|
sub |
用户ID | 识别特定用户 |
email |
用户邮箱 | 如果硬编码,暴露PII |
role |
权限级别 | service_role 是关键 |
exp |
过期时间 | 过期令牌风险较低 |
iat |
签发时间 | 指示创建时间 |
Supabase特定声明
| 声明 | 描述 |
|---|---|
ref |
项目引用 |
iss |
应为“supabase” |
aal |
认证保证级别 |
amr |
使用的认证方法 |
安全发现
P0 - 关键
🔴 服务角色密钥暴露(角色: service_role)
→ 需要立即密钥轮换
P1 - 高
🟠 硬编码用户令牌带有PII(邮箱、sub可见)
→ 从代码中移除,可能需要通知用户
P2 - 中
🟡 代码中过期的测试令牌
→ 清理,潜在信息泄露
上下文输出
保存到 .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字符串。此技能验证结构。
硬编码令牌的修复
之前(错误)
// ❌ 永远不要硬编码用户令牌
const adminToken = 'eyJhbGciOiJIUzI1NiI...'
fetch('/api/admin', {
headers: { Authorization: `Bearer ${adminToken}` }
})
之后(正确)
// ✅ 从Supabase会话获取令牌
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:[时间戳] [supabase-extract-jwt] [开始] 开始JWT提取 [时间戳] [supabase-extract-jwt] [成功] 找到3个JWT [时间戳] [supabase-extract-jwt] [上下文更新] .sb-pentest-context.json 更新 -
如果文件不存在,在写入前创建。
未能更新上下文文件是不可接受的。
强制:证据收集
📁 证据目录: .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": "硬编码用户令牌带有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— 分析认证配置