security-audit
検証済みv0.3.0社内展開前のWebツールセキュリティ監査。段階承認方式で GO/CONDITIONAL/NO-GO を判定する。
- トリガー: 「セキュリティチェックして」「展開前監査」「社内展開前チェック」「OWASP診断」「ペネトレ前チェック」「security-audit」。
- 対象: 展開候補ツールのURL(+推奨: リポジトリ)と脅威モデル(社内限定/顧客向け/公開)。
- NOT for: PR単位の差分セキュリティレビュー(→ /security-review)、セキュリティに限らないコードレビュー全般(→ /multi-model-review)。
Security Audit Skill
社内グループ展開前のWebツールセキュリティ監査を段階承認方式で実施する。
哲学(最重要)
このスキルは「展開してよいか」の判断材料を提供する。誤った断定は許容されない。観測した事実 ≠ 結論。各指摘に必ずラベルを付ける:
[観測]HTTPレスポンスやコードで直接確認できた事実[推定]観測から導いた仮説(誤りの可能性あり)[要確認]検証していない・できなかった項目(理由を明記)
ラベル併用可。例: 「[観測] hd=domain あり → [推定] UI ヒント有効 → [要確認] サーバ側 callback でも検証されているか」。観測した防御が実は不十分というケースで重要。
過去の失敗事例: OAuth 認可URLに hd= パラメータが無いことだけを根拠に「ドメイン制限なし」と断定したが、実際はサーバ側 signIn callback で弾いていた。Google認可URLのhd=はUIヒントに過ぎない。サーバ側実装を確認するまで「制限なし」とは言わない。
関連スキルとの住み分け
/security-audit(本スキル): ツール全体の展開前監査(パッシブ + 認証フロー + コード + 判定 + 改善コード)。最終判定 GO/NO-GO。/security-review: PR単位の差分セキュリティレビュー(Anthropic公式、設定変更ベース)。/multi-model-review: 4モデル並列のコードレビュー全般。セキュリティ単独ではない。/dogfood: UX・バグ探索。セキュリティ目的ではない。/codex-review-loop: Codex の動的検証 + 修正ループ。
本スキルは 「ツールを社内に出していいか」を判断する スキル。PR単位なら /security-review、コード品質広範なら /multi-model-review。
トリガー
- 「セキュリティチェックして」
- 「社内展開前にチェック」
- 「展開していい?」
- 「security audit」「OWASP診断」「ペネトレ前チェック」
- 明示的:
/security-audit <URL> [--repo <github-or-path>]
入力
| 項目 | 必須 | 例 |
|---|---|---|
| URL | ◯ | https://your-tool.vercel.app |
| リポジトリ | 推奨 | GitHub URL or ローカルパス(コード監査の精度激増) |
| 脅威モデル | ◯ | 社内限定 / 顧客向け / 公開 |
| アクティブスキャン許可 | Phase 4 で改めて確認 | yes/no |
前提コマンド
実行ホストに以下が必要:
| コマンド | 用途 | 必須? |
|---|---|---|
curl | HTTP リクエスト | 必須 |
openssl | TLS 確認 | 推奨 |
python3 | JSON pretty print | 推奨 |
jq | JSON 解析 | 推奨 |
gh | GitHub リポジトリ取得 | リポジトリ提供時 |
gitleaks | git 履歴シークレットスキャン | 推奨(fallback あり) |
npx | npm audit, web-ext lint | リポジトリ提供時 |
clasp | GAS 監査時 | GAS 監査時のみ |
bash | 4.0+ 推奨(macOS デフォルトの 3.2 でも動作確認済) | 必須 |
実行フロー
Phase 0: スコープ確認 (phases/00-scope.md)
→ 所有確認 / 脅威モデル / 監査範囲 / 監査計画提示
⛔ 所有確認なしで Phase 1 に進まない
⚠️ Phase 4 はこの段階では承認対象外(Phase 4 開始時に改めて確認)
Phase 1: パッシブ観測 (phases/01-passive.md)
→ HTTPヘッダ / Cookie / TLS / シークレットgrep / フレームワーク
→ 第三者サイトでも実行可能(読取のみ)
→ 既知公開パスのみ確認 (5件以下)、列挙的試行は Phase 4 で
Phase 2: 認証フロー観測 (phases/02-auth-flow.md)
→ OAuth設定 / CSRF / リダイレクト挙動
→ 無認証で観測可能な範囲
Phase 3: コード監査 (phases/03-code-audit.md)
→ リポジトリ提供時のみ
→ 対象別チェックリスト適用 (checklists/)
→ Phase 0 のスタック特定と差異があれば計画を更新
Phase 4: アクティブスキャン (phases/04-active.md)
⛔ Phase 0 とは別に明示再承認必須
⛔ 第三者サイト不可
→ パス列挙 / API認可マトリクス / CORS / Rate limit
Phase 5: 判定 + 改善 (phases/05-verdict.md)
→ GO / CONDITIONAL GO / NO-GO
→ クリティカル / 中 / 低 / 未検証
→ 修正コードスニペット (fixes/)
→ templates/report.md フォーマットで出力
対象別チェックリスト
実行時、対象スタックに該当するものを Phase 3 で必要なものだけ 読込む(progressive disclosure):
全監査で必須(スタック非依存)
- 設計・設定インベントリ →
checklists/design-config-inventory.md(構成・データ・認可・secret・CI/CD・運用の棚卸し。未取得 → CONDITIONAL GO 確定) - 高機密データ取扱 →
checklists/sensitive-data-handling.md(A 区分: マイナンバー / 決済 / 本人確認書類 / 医療 / 契約書 等を扱う場合に必須) - インフラ / CI/CD / Secret →
checklists/infra-cicd-secrets.md(GitHub Actions secrets / OIDC / Vercel env / DNS)
スタック別
- Vercel/Next.js + OAuth (NextAuth.js) →
checklists/vercel-nextjs-oauth.md - Supabase (RLS / Edge Functions) →
checklists/supabase.md(アクセスマトリクス G 節 必須) - Chrome拡張 →
checklists/chrome-extension.md - GAS / WebApp →
checklists/gas-webapp.md
機能別(該当時必須)
- Object Storage / CDN →
checklists/object-storage-cdn.md(bucket / 署名 URL / 変換 URL / 削除 / purge) - SSR / CDN 動的キャッシュ →
checklists/ssr-cdn-cache.md(認証ページの cache 漏洩。SSR/ISR/Edge cache 使用時必須)
複数スタックなら該当全て。Phase 0 で stack を確定 → Phase 3 で読込。
新スタック (Cloudflare Workers / Hono / Astro 等) 追加時は README.md「拡張ガイド」 を参照。
判定基準(Single Source of Truth)
判定の唯一の正本はこのセクション。templates/report.md phases/05-verdict.md README.md は本セクションを参照する。
判定ロジック
critical_count = クリティカル課題の確定件数
unverified_critical_count = クリティカル定義に該当するが未検証の項目数
medium_count = 中課題の件数
threat_model = "internal" | "customer" | "public"
inventory_complete = 設計・設定インベントリ全項目埋まっている (checklists/design-config-inventory.md)
sensitive_data_complete = 高機密データ checklist 完了 or 非該当 (checklists/sensitive-data-handling.md)
IF critical_count > 0:
→ NO-GO
ELSE IF NOT inventory_complete:
→ CONDITIONAL GO (条件: 設計・設定インベントリ完成)
理由: 公開面・データ流路・最強権限の所在が未確認のまま GO 判定は出せない
ELSE IF NOT sensitive_data_complete:
→ CONDITIONAL GO (条件: 高機密データ checklist 完了)
ELSE IF unverified_critical_count > 0:
→ CONDITIONAL GO (条件: 該当未検証項目の検証完了)
ELSE IF (medium_count >= 3) OR (threat_model == "public" AND medium_count >= 1):
→ CONDITIONAL GO (条件: 中課題の修正完了、または許容根拠の明記)
ELSE:
→ GO
判定の意味の明示: GO は 「実施した監査範囲内で展開ブロッカーが見つからなかった」 という意味であり、「インフラ・DB 設計・全運用設定まで含めて安全」を保証するものではない。レポート冒頭に必ず以下を明示する:
本監査は実施 phase(Phase 1-5)と適用 checklist の範囲内での評価です。範囲外(物理セキュリティ / 法令コンプライアンス監査 / ペネトレーションテスト / 第三者ライブラリ内部実装 等)は対象外であり、
GO判定はそれら範囲外を含む全体安全を保証しません。
機密性高での昇格
データ機密性 = High(PII / 医療 / 金融 / 契約)の場合、以下の中課題はクリティカル昇格:
- API Rate Limit 欠如 (アップロード / 認証エンドポイント)
- ログにPII残存
- LLM への学習対象モデル送信
- セッション期限 30日超
- CSP 完全欠落
中課題のグループ化
「セキュリティヘッダ群」(CSP / X-Frame-Options / X-Content-Type-Options / Referrer-Policy / Permissions-Policy) は1つでも欠落していれば1グループとして1中課題カウント(複数欠落で件数水増ししない)。
クリティカル定義
以下のうちいずれかが該当すれば展開ブロッカー。各項目には観測条件と確認手段を記載し、観測のみで断定しない(断定禁止原則):
| # | 課題 | 観測 | 必要な確認 |
|---|---|---|---|
| 1 | 認証ガード欠落 | /api/* 等保護対象が無認証で 200 を返す | middleware の matcher 確認 |
| 2 | 認可破綻 (IDOR) | user-A セッションで user-B のリソースID にアクセス成功 | DB query / route.ts コード確認 |
| 3 | 権限昇格 | 一般ロールで管理画面 / 管理 API にアクセス成功 | role check コード確認 |
| 4 | シークレット漏洩 | HTML/JS バンドル/git履歴で API key/JWT 検出 | base64 decode で payload 確認 (anon vs service_role) |
| 5 | ドメイン制限不備 (社内ツール) | 外部ドメインアカウントでログイン成功 | signIn callback コード + GCP Internal 設定 確認 |
| 6 | SQLi / XSS / CSRF / Open Redirect / SSRF / SSTI 実証 | 実際にペイロードが反射実行される | コード確認 + payload 試行 |
| 7 | Mass Assignment | PATCH で role/userId/createdBy が書き換わる | route ハンドラの allowlist パース確認 |
| 8 | JWT alg=none / 署名検証 skip | カスタム JWT 実装でアルゴリズム強制無し | jsonwebtoken / jose ライブラリの options 確認 |
| 9 | ファイルアップロード検証欠落 | 拡張子偽装/path traversal/zip slip が通る | route ハンドラのコード確認 |
| 10 | RLS 無効化 (Supabase) | pg_tables.rowsecurity = false のテーブル + データあり | SQL select rowsecurity from pg_tables |
| 11 | Chrome拡張 <all_urls> + リモートコード | manifest + webRequestBlocking や remote script | manifest.json + ソース確認 |
| 12 | GAS WebApp Anyone, even anonymous + 認可なし | デプロイ設定 + コード | デプロイ画面ヒアリング + コード Session.getActiveUser 確認 |
| 13 | 高機密データ (マイナンバー / 決済 / 本人確認書類 / 医療 / 契約書) のログ・trace・分析基盤・学習対象LLM への流入 | console.log / Sentry / Datadog / GA / ChatGPT consumer 等への送信 | checklists/sensitive-data-handling.md B/C 確認 |
| 14 | ブラウザ storage に認証 token | localStorage / sessionStorage / IndexedDB / SW cache に token / accessToken / jwt 等 | コード grep + DevTools 観測 |
| 15 | Storage bucket / 署名 URL の公開不備 | public bucket に機密ファイル / 署名 URL 長期 / 連番 path / 削除後 CDN 残存 | checklists/object-storage-cdn.md 確認 |
| 16 | SSR / CDN 動的キャッシュでユーザ間漏洩 | 認証ページ・ユーザ別 API が共有 cache に乗る (x-vercel-cache: HIT で別 cookie 同応答) | checklists/ssr-cdn-cache.md 確認 |
| 17 | CI/CD 経由の本番権限露出 | GitHub Actions に service_role / 本番 DB credential / 長期 access key + pull_request_target 等で露出経路 | checklists/infra-cicd-secrets.md B/C 確認 |
重要: 観測列だけでクリティカル断定しない。確認手段の結果が出てから判定。確認できない場合は「未検証」扱い。
未検証クリティカル候補の判定への影響
以下の checklist 未取得 / 未完了は クリティカル定義に該当する項目が確認不能 であることを意味するため、unverified_critical_count に加算される(自動的に CONDITIONAL GO 以下):
| checklist | 未取得時の扱い |
|---|---|
checklists/design-config-inventory.md 全節 | 公開面 / データ分類 / 最強権限の所在 未確認 → 1件以上 |
checklists/sensitive-data-handling.md(A 区分扱う場合) | クリティカル #13 未検証 |
checklists/object-storage-cdn.md(Storage 使用時) | クリティカル #15 未検証 |
checklists/ssr-cdn-cache.md(SSR/ISR/Edge cache 使用時) | クリティカル #16 未検証 |
checklists/infra-cicd-secrets.md(CI/CD 経由 deploy 時) | クリティカル #17 未検証 |
| Supabase アクセスマトリクス G1 | クリティカル #1, #2, #4, #10 のいずれか未検証扱い |
| Cookie / Browser storage 棚卸し | クリティカル #14 未検証 |
中課題定義
展開前に修正推奨だが、ブロッカーではない:
- セキュリティヘッダ欠落(CSP / X-Frame-Options / X-Content-Type-Options / Referrer-Policy / Permissions-Policy)← 1グループ
- API未認可応答が HTML 307 リダイレクト(401 JSON 推奨)
- Rate limit なし(ログイン / アップロード等)
- セッション期限長すぎ
- ログにPII残存(機密性高なら昇格)
- 依存ライブラリ高脆弱性(npm audit high以上)
trustHost: true盲目設定(Host header injection リスク)- Account Linking 攻撃の可能性 (
allowDangerousEmailAccountLinking: true) - ログイン成功時セッション再生成なし
- パスワード比較がタイミングセーフでない
- Subresource Integrity 未設定 (外部スクリプト使用時)
低課題定義
後追い対応可:
x-powered-by露出hd=パラメータ未付与(サーバ側で弾いていれば実害なし、UX改善)- ログ整形 / 監視ダッシュボード未整備(脅威モデル「公開」では中以上に昇格)
出力フォーマット
templates/report.md を厳守。判定セクションは grep 可能な機械フォーマット。
アクティブスキャン承認の扱い
第三者サイトへのアクティブスキャンは Bash ハーネスが拒否することがある。Phase 0 で所有確認できても、Phase 4 で改めてユーザに確認する:
Phase 4 アクティブスキャン許可しますか? 以下を実行します:
- パス列挙(API endpoints、~50項目以下、generic のみ)
- 認可マトリクス(無認証 / 別ロール / 別ユーザ)
- CORS preflight
- Rate limit 試行(5回/分以下)
許可なしなら Phase 4 をスキップして「アクティブ未実施」を未検証項目に明記。
法的考慮
日本の不正アクセス禁止法 第3条: 業務上の正当な範囲を超える試行は違法。本スキルでは:
- Phase 0 で所有確認 + 監査同意を取得(自己申告だが必須)
- Phase 4 では
--confirmedフラグ +AUDIT_TARGET_URL環境変数で URL ロック - アクティブスキャン許可は Phase 0 の包括承認に含めず、Phase 4 で改めて取得
- IDOR 検証は テストアカウント間 + 本人同意 を前提
ペネトレーションテスト(Bug Bounty 相当の exploit chain 実証)は本スキルの範囲外。本スキルは pre-deployment セキュリティ check。
自動化の制約
- Passive (Phase 1-2) は自動化可
- Phase 3-4 は人間判断必須 (LaunchAgent /
/scheduleで月次自動化する場合は Phase 1-2 のみ) - 修正適用は自動化禁止 (PR作成までは可、merge は人間)
禁止事項
- DoS / brute force / 大量トラフィック投入
- 第三者サイトへの権限なきスキャン
- 既知ユーザ認証情報の使用(必ず本人がログインしたセッションを使う)
- 修正PR の自動マージ(必ずレビュー経由)
- 自動コミット(修正適用は1件ずつユーザ確認)
- 「展開可」と断定して未検証項目を隠蔽すること
改善コード提示原則
fixes/ 配下のスニペットはコピペで動く完全形を維持する。修正提示時:
- 該当ファイル名を推定(提供リポジトリから or 一般的な配置)
- 修正前 / 修正後 の差分を提示
- 検証方法を明記(どのコマンド・どの操作で確認できるか)
- 該当ライブラリのバージョン (NextAuth v5 / v4 等) を明示
終了条件
Phase 5 完了 → レポート生成 → ユーザに引き渡し。 ユーザが「修正を一緒に進めたい」と言えば、修正適用フェーズへ遷移可。自動適用は禁止。
拡張ガイド
新スタック追加時は以下5箇所同時更新:
checklists/<X>.md新規作成fixes/<X>-*.md修正コード追加SKILL.mdの「対象別チェックリスト」セクションに追加phases/00-scope.mdの技術スタック確認セクションphases/03-code-audit.mdの自動判定ロジック
詳細は README.md「拡張ガイド」参照。
変更履歴(最新)
- 0.3.0 (2026-05-11): 外部レビュー反映で 5 つの新規 checklist と判定ロジック改定
- 設計・設定インベントリ / 高機密データ取扱 / Object Storage CDN / SSR-CDN cache / インフラ-CI/CD-Secrets を分離
- クリティカル定義 12 → 17 に拡張
GOの意味を「実施範囲内」に限定する記述を必須化- 詳細は
README.md「バージョン」参照