supabase-extract-jwt について
クライアント側コードに含まれるSupabase関連JWTを検出・分析するセキュリティスキルです。トークンのクレームや保存先の参照を調べ、露出したキー、ハードコードされたユーザートークン、認証情報の把握に役立ちます。
主な機能
- 01クライアントコード内のJWTとAPIキーの検出
- 02トークンクレームと有効期限の分析
- 03ハードコードされたユーザートークンと個人情報の検出
- 04JWTの保存先参照の特定
- 05検出結果と抽出証拠の保存
活用シーン
- 01クライアントコードのservice roleキー露出確認
- 02ハードコードされたユーザートークンの調査
- 03トークンクレームと有効期限の確認
- 04認証関連コードのパターン把握
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yoanbernabeu/ supabase-pentest-skills --skill supabase-extract-jwtインストール後、Claude Code で/supabase-extract-jwtを入力して読み込まれたか確認します。
詳細
Supabase JWT の抽出
🔴 重大:ファイルの段階的な更新が必須です
コンテキストファイルには、最後だけでなく作業中に随時書き込む必要があります。
- 発見するたびに、直ちに
.sb-pentest-context.jsonに書き込んでください- 各アクションの前後に
.sb-pentest-audit.logに記録してください- スキルが完了するまでファイルの更新を待たないでください
- スキルがクラッシュしたり中断されたりしても、それまでのすべての調査結果がすでに保存されている必要があります
これは任意ではありません。段階的な書き込みを怠ると重大なエラーになります。
このスキルは、クライアント側のコードに関連する Supabase の JSON Web Token(JWT)を抽出して分析します。
このスキルを使用する場面
- クライアントコードに公開されているすべての JWT トークンを見つける場合
- トークンのクレームと有効期限を分析する場合
- ハードコードされたユーザートークン(セキュリティ上の問題)を検出する場合
- 認証フローを把握する場合
前提条件
- 対象のアプリケーションにアクセスできること
- Supabase の検出が完了していること(必要に応じて自動的に実行されます)
Supabase の JWT の種類
| 種類 | 用途 | クライアントへの公開 |
|---|---|---|
| Anon Key | API 認証 | ✅ 想定どおり |
| Service Role Key | 管理者アクセス | ❌ 決して公開しない |
| Access Token | ユーザーセッション | ⚠️ 動的な場合のみ |
| Refresh Token | トークンの更新 | ⚠️ 動的な場合のみ |
検出パターン
1. API キー(静的)
// Supabase API keys are JWTs
const SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
2. ハードコードされたユーザートークン(問題あり)
// ❌ Should never be hardcoded
const userToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiZW1haWwiOiJ1c2VyQGV4YW1wbGUuY29tIn0...'
3. ストレージキーのパターン
// 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: [email protected]
├── 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.
═══════════════════════════════════════════════════════════
JWT クレームの分析
このスキルは主要なクレームを特定します。
標準クレーム
| クレーム | 説明 | セキュリティへの影響 |
|---|---|---|
sub | ユーザー ID | 特定のユーザーを識別します |
email | ユーザーのメールアドレス | ハードコードされている場合、個人情報(PII)が漏えいします |
role | 権限レベル | service_role は重大な問題です |
exp | 有効期限 | 期限切れのトークンはリスクが低くなります |
iat | 発行時刻 | 作成時期を示します |
Supabase 固有のクレーム
| クレーム | 説明 |
|---|---|
ref | プロジェクト参照 |
iss | 「supabase」である必要があります |
aal | 認証器保証レベル |
amr | 使用された認証方式 |
セキュリティ上の検出事項
P0 - 重大
🔴 Service role key exposed (role: service_role)
→ Immediate key rotation required
P1 - 高
🟠 User token hardcoded with PII (email, sub visible)
→ Remove from code, may need to notify user
P2 - 中
🟡 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": "[email protected]",
"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— anon key を特定して抽出しますsupabase-extract-service-key— service key の有無を確認します(重要)supabase-audit-auth-config— 認証設定を分析します