auditing-code-for-vulnerabilities について
auditing-code-for-vulnerabilitiesは、リポジトリ、変更、スキャナーの検出結果を調べる担当者向けのソースコード監査手法です。セキュリティ上の前提を整理し、攻撃面を追跡して悪用可能性を検証し、単なるパターン照合にとどまらず類似箇所も探します。
主な機能
- 01資産と信頼境界のモデル化
- 02入力ポイントの特定と優先順位付け
- 03データ経路に沿った脆弱性追跡
- 04到達性・制御・影響の検証
- 05確認済み脆弱性の類似箇所検索
- 06監査範囲の記録
活用シーン
- 01サービスやリポジトリの脆弱性監査
- 02変更やプルリクエストのリスクレビュー
- 03スキャナーの警告が実際の問題か検証
- 04顧客向けセキュリティ監査の計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ trilwu/ secskills --skill auditing-code-for-vulnerabilitiesインストール後、Claude Code で/auditing-code-for-vulnerabilitiesを入力して読み込まれたか確認します。
同梱ファイルreferences/bug-class-checklist.md
詳細
脆弱性を見つけるためのコード監査
ソースコードから実際のバグを見つけることは、スキャナーを実行することとは異なる仕事です。スキャナーはパターンに一致するものを探します。一方、監査者はコードが本来保証すべきことをモデル化し、その保証が破られる経路を探します。このスキルでは、後者の仕事の手法を説明します。
使用する場合
- リポジトリ、サービス、ライブラリを監査し、セキュリティ上の欠陥を探す場合
- 差分、ブランチ、プルリクエストをレビューし、新たに持ち込まれた脆弱性を探す場合
- 大規模なコードベース全体から、特定の種類のバグを探す場合
- スキャナーの検出結果や報告された脆弱性が実在するかどうかを検証する場合
- クライアントとの業務に向けて、監査計画とカバレッジレポートを作成する場合
使用しない場合
- 実行中のアプリのブラックボックステスト —
testing-web-applicationsまたはtesting-apisを使用してください - 特に PHP のコードベース(型ジャグリング、
unserialize/phar の POP チェーン、php:// ラッパー)— この手法とauditing-php-applicationsを併用してください - 脆弱性ではなく、仕込まれたウェブシェルやバックドアを探す場合 —
hunting-web-backdoorsを使用してください - バイナリのみが対象の場合 —
analyzing-binariesを使用してください - 依存関係やビルドパイプラインのリスク —
auditing-supply-chainを使用してください - 暗号方式のレビュー —
reviewing-cryptographyを使用してください - 確定した検出結果を報告書にまとめる場合 —
reporting-security-findingsを使用してください - ビルダーと批評役を分けて反復しながら、対象全体を複数のエージェントで調査する場合 —
orchestrating-vulnerability-researchを使用してください。このスキルをスライスごとのハンターとして呼び出します
監査のループ
監査は1回ではなく、4つのパスで行います。パス3から始めてはいけません。
1. Context → what does this system protect, and from whom?
2. Attack surface → where does untrusted input enter, and what does it reach?
3. Hunt → trace specific bug classes along those paths
4. Verify → prove exploitability before you write a word
パス1:コードを読む前にコンテキストを把握する
ファイルを手当たり次第に開いてはいけません。最初の作業時間を使って、次の問いに答えてください。
| 質問 | 調査先 |
|---|---|
| セキュリティ上重要な資産は何ですか? | README、ドキュメント、データモデル、DB スキーマ |
| 利用者と信頼レベルはどうなっていますか? | 認証ミドルウェア、ロールの列挙型、テナントモデル |
| 明示されている不変条件は何ですか? | テスト、アサーション、「must」「never」「invariant」を含むコメント |
| ここで過去に修正されたものは何ですか? | セキュリティ関連キーワードを使った git log --grep、CVE ファイル、SECURITY.md |
| 対象範囲外は何ですか? | 業務概要、ベンダーコード、生成ファイル |
# Prior security work is the cheapest source of bug leads
git log --oneline --grep='security\|CVE\|vuln\|injection\|auth bypass\|overflow' -i | head -40
# Stated invariants often mark where the author was nervous
rg -n --stats 'MUST NOT|must never|SECURITY|XXX|HACK|TODO.*(auth|secur|valid)' -i
# Where does privilege actually get checked?
rg -n 'is_admin|require_role|authorize|has_permission|@login_required|checkAccess'
探索を始める前に、資産、利用者、信頼境界、そして破られると最も重大な3つの不変条件を含む、対象のモデルを簡潔に作成してください。この後の作業はすべて、これらの不変条件に反する例を探すことになります。
パス2:攻撃対象領域を把握する
エントリーポイントを列挙し、優先順位を付けてください。エントリーポイントの重要度は、検証されるまでにどれほど深く到達できるかに比例します。
# HTTP/RPC routes
rg -n '@(app|router)\.(get|post|put|delete|patch)|app\.(get|post)\(|@RequestMapping|http\.HandleFunc'
# Deserialization, template rendering, and dynamic execution sinks
rg -n 'pickle\.loads|yaml\.load\(|Marshal|unserialize|ObjectInputStream|eval\(|new Function|exec\(|Runtime\.getRuntime'
# Command, SQL, and path sinks
rg -n 'os\.system|subprocess.*shell\s*=\s*True|child_process\.exec\(|execSync|Statement\.execute|\.raw\(|fmt\.Sprintf.*SELECT'
# Where authentication is decided rather than enforced
rg -n 'verify=False|InsecureSkipVerify|jwt\.decode\(.*verify.*False|algorithms=\[.*none'
エントリーポイントは、次の優先順位で分類してください。認証なしで到達可能なもの > 低権限の利用者が到達可能なもの > 管理者のみが到達可能なもの。その後、優先順位が最も高いものから内部へたどってください。幅より深さが重要です。1つの経路を完全に追跡するほうが、grep のヒットを20件確認するよりも価値があります。
grep のヒットは候補であり、検出結果ではありません。 上記のコマンドは、低コストで広く網をかけるためのものです。各一致箇所は、追跡が済むまで未解決の手がかりです。候補の一覧((file:line, bug class, entry point) の作業リスト)を保持し、各項目に対して「安全と確認済み」「脆弱性を確認済み」「PoC が必要」のいずれかを明示してください。まず低コストで網を広げ、その後、候補ごとにコストをかけて推論します。逆の順序にしてはいけません。失敗の原因は grep パターンの見落としではありません。候補を50件列挙し、5件をざっと見ただけで、コードベース全体に問題はないと判断することです。大規模なコードベースでは、プロジェクト独自の慣例も検索対象に含めてください。たとえば、ORM の生クエリを使う抜け道、認証デコレーターの名前、テンプレート呼び出しなどです。一般的なパターンでは見つからない箇所こそ、最も多くの問題が見つかるポイントだからです。
パス 3:たどった経路に沿ってバグの種類を探す
有望な経路ごとに、ソースからシンクまでの汚染をたどり、コードが何を前提としているかを確認します。本番環境まで残る頻度がおおよそ高い順に挙げると、特に見つかりやすいのは次の種類です。
認証ではなく認可。 実際の侵害のほとんどは、ログインの破綻ではなく、オブジェクトレベルのチェック漏れです。IDを受け取るハンドラーごとに、オブジェクトが呼び出し元のテナント/ユーザーに紐付けられているか、それともIDだけで検索されているかを確認します。デコレーターではなく、クエリを確認してください。
信頼境界の混同。 あるレイヤーで検証されたデータが、別のレイヤーで再解析されます。シリアライズ境界を越える値を探します。たとえば、検証済みの文字列がURL、パス、テンプレート、クエリとして再解析される場合です。
状態と並行処理。 チェック後に使用するまでの隙間、非アトミックな残高更新、実際には一意でない冪等性キー、副作用を再実行するリトライ経路を確認します。ロックやトランザクションなしで、読み取りの後に書き込みを行う箇所を検索します。
二次インタープリターへのインジェクション。 SQL、シェル、LDAP、XPath、テンプレートエンジン、ログ形式、正規表現が対象です。問うべきなのは「フィルターがあるか」ではなく、「フィルターとインタープリターが同じ文法を前提としているか」です。
メモリー安全性(C/C++/unsafe Rust/CGo)。境界チェック前の長さ計算、攻撃者の影響を受けるサイズを指定した memcpy、ループ境界のオフバイワン、符号付き/符号なし変換、エラー経路での解放後使用を確認します。
エラー処理とクリーンアップの経路。 通常の成功経路はレビューされていることが多い一方、except、catch、defer、goto fail の分岐は見落とされがちです。これらを重点的に監査してください。
シークレットと暗号の誤用。 ハードコードされたキー、トークンの非定数時間比較、Math.random/rand() による予測可能なID、署名検証の欠落が該当します。暗号の詳細なレビューは reviewing-cryptography で行います。
パス 4:類似箇所の分析
バグはテンプレートであり、単発のインシデントではありません。ひとつ確認したら、すぐに類似箇所を検索します。同じ作者による同じミス、コピーされた同じコードブロック、隣接するルートにおける同じチェック漏れを探してください。
# You found one unscoped lookup. Find every other one.
rg -n 'find_by_id|findOne\(\{ *_id|get_object_or_404' -A3 | rg -v 'tenant|owner|user_id'
類似箇所の分析は、監査の価値を大きく高めます。時間を明示的に割り当て、確認した指摘事項1件につき、おおよそ同じ時間を類似箇所の検索に充ててください。
報告前の検証
実証できない指摘事項は仮説にすぎません。報告に含める前に、次の4点すべてに答えてください。
- 到達可能性 — 具体的なエントリーポイントと、呼び出し元に必要な権限を明示します。「認証なしで
POST /api/exportにアクセスできる攻撃者」。 - 制御可能性 — 危険な値のどの部分を攻撃者が制御できるのかを示します。
- 影響 — 何が破綻するのかを述べます。パス 1 で特定した不変条件のうちどれか、そして攻撃者が何を得るのかを明示します。
- 緩和策がないこと — WAFルール、フレームワークのデフォルト設定、ミドルウェア、DB制約、呼び出し元によるサニタイズがすでにないか確認します。
概念実証が対象範囲に含まれる場合は、武器化したエクスプロイトではなく、シンクを制御できることを証明する最小限のものを書いてください。
再検証して誤検知を除外する
上記の4つのチェックは指摘事項を確認するためのものですが、この工程ではそれを否定しようとします。確認済みの候補すべてに対して、報告に含める前に実施してください。報告の信頼性は、最良の真の指摘ではなく、最悪の誤検知によって決まります。
-
すでに修正されていませんか? 読んでいるツリーが修正より古い場合や、まだ取得していないブランチに修正が含まれている場合があります。報告する前に、脆弱なコードが実際に出荷されるものかを確認してください。
git log -S'<dangerous token>' --oneline -- <file> # when this line changed, and toward what git log --oneline <checkout>..origin/main -- <file> # a fix on main you are not reading git blame -L <line>,<line> <file> # the commit that introduced it, for context -
敵対的な視点から再検証します。 反対のケースを論じます。コードは安全だと仮定し、それを安全にする制御策、つまりミドルウェア、DB制約、呼び出し元によるサニタイズがないか探してください。本気で反証を試みても残る指摘事項なら、自信を持って説明できます。
-
シンクが引き続きその値を受け取ることを確認します。 最後の区間をもう一度たどってください。リファクタリングによって、ソースとシンクの間にバリデーターやエンコーダーが挿入されていても、最初の読み取りでは見落としがちです。
ここで否定できたものは除外し、その旨をカバレッジのメモに記載してください。再検証できない候補は、自分用のメモであり、指摘事項ではありません。
却下すべき合理化
こうした考えが、監査を形式的な作業に変えてしまいます。どれも誤りです。
- 「おそらく上流で検証済みです。」 では、上流を確認してください。呼び出し元について未検証のまま置かれた前提は、バグの見落としにつながる原因として最もよく見られます。
- 「その処理はフレームワークが担当しています。」 フレームワークが処理するのはデフォルトの経路です。バージョンと設定を確認し、この呼び出しが安全な API を使用しているかを確かめてください。
- 「その入力は内部のものです。」 現時点では内部のものかもしれません。入力元をたどってください。キューのコンシューマーや管理者によるインポートは、たいてい外部から到達可能です。
- 「認証済みユーザーしか悪用できません。」 それは深刻度の判断材料であって、指摘を取り下げる理由ではありません。
- 「意図的な実装に見えます。」 意図は対策ではありません。その意図を記録し、指摘は残してください。
- 「スキャナーでは検出されませんでした。」 スキャナーが検出できるのは、ルールがあるものだけです。
- 「確認できたので、実在する問題です。」 確認したのは、あるチェックアウトと自分の最初の読み取りに対してです。報告書に含める前に、実際に出荷されるものに対して再検証し、本気で反証を試みてください。
- 「十分な数のファイルを確認しました。」 カバレッジはファイル数ではなく、Pass 2 でマッピングした攻撃対象領域を基準に測定します。
ツールによる支援であり、ツールによる代替ではありません
静的解析は、カバレッジの確保と、パターンを把握した後の類似箇所の検索に使います。バグを確認したらルールを作成し、残りの箇所を検出させてください。
# Semgrep: broad pass, then a rule you write for your specific finding
semgrep --config=auto --severity=ERROR --json -o semgrep.json .
semgrep --config=./rules/my-variant-rule.yaml .
# CodeQL for dataflow questions grep cannot answer
codeql database create db --language=<lang> && codeql database analyze db --format=sarif-latest -o out.sarif
# Language-specific
bandit -r . -f json # Python
gosec -fmt=json ./... # Go
cargo audit && cargo geiger # Rust deps + unsafe surface
npm audit --json # JS deps
ツールによる指摘はすべて、上記の4つの検証質問を通してトリアージしてください。検証されていないスキャナー出力を報告するのは、報告しないよりも悪いことです。読者の信頼を損ない、本当のバグを埋もれさせてしまいます。
監査の成果物
作業を進めながらカバレッジを記録し、正直に報告してください。
## Coverage
| Component | Files | Depth | Notes |
|-----------|-------|------------|--------------------------------|
| auth/ | 12 | Full trace | All routes traced to sinks |
| billing/ | 30 | Partial | Webhook handlers only |
| vendor/ | - | Excluded | Out of scope per brief |
## Findings
F1. [High] Tenant isolation bypass in GET /api/reports/:id — <impact> — <repro>
対象にしなかった範囲を明記してください。達成していないのに完全なカバレッジを主張する監査は、作成し得る成果物の中で最も有害なものです。
参考資料
references/bug-class-checklist.md— 言語ごとの脆弱性調査チェックリストreporting-security-findings— 深刻度の評価と報告書の形式- 要件に基づくレビューには OWASP Application Security Verification Standard(ASVS)
- 分類には CWE Top 25 と CWE の階層