Claude Codeで脅威モデリング — STRIDEをデータフロー図から当てる手順
リポジトリから信頼境界つきのデータフロー図をMermaidで起こし、境界ごとにSTRIDEの6区分を当てて脅威表にし、対策済みかどうかをコードの該当行で裏取りする流れを、プランモードと読み取り専用サブエージェントで組みます。
脅威モデリングは、守りたいものを決め、何が起こりうるかを洗い出し、対策を決め、できているかを確かめる作業です。Claude Codeはリポジトリを読めるので、最初の下書きである「データフロー図」と「脅威表」を短時間で起こせます。残りは人が判断します。
この記事では、自社アプリのリポジトリに対してデータフロー図(DFD)をMermaidで描かせ、信頼境界ごとにSTRIDEの6区分を当て、各脅威が対策済みか未対策かをコードの行番号で裏取りするところまでを手順にします。MCPやゲートウェイなど特定製品の脅威モデルの読み解きはMCPの脅威モデルやClaude apps gatewayの脅威モデルが扱っています。ここでは自分のコードに当てる側の手順です。
脅威モデリングの4問とClaude Codeの分担
OWASPのコミュニティページは、脅威モデリングを4つの問いで整理しています。
- 何に取り組んでいるのか(対象の範囲)
- 何が起こりうるか
- それに対して何をするか
- うまくできたか
2つ目の問いの手法として、同ページはSTRIDE、キルチェーン、アタックツリーを例に挙げています。ほかに「正しい」やり方が一つに決まっているわけではない、とも書かれています。
Claude Codeに任せやすいのは、1問目(コードから対象を起こす)と、4問目の一部(対策がコードにあるかの確認)です。2問目と3問目は下書きまでは任せられますが、リスクを受け入れるか、移すか、なくすかの判断は人の仕事です。
| 問い | Claude Codeに任せる範囲 | 人が決めること |
|---|---|---|
| 何に取り組んでいるか | Claude Codeに任せる範囲ルート・外部呼び出し・DBアクセスの棚卸しとDFDの下書き | 人が決めること守る資産と範囲の決定 |
| 何が起こりうるか | Claude Codeに任せる範囲境界ごとのSTRIDE候補の列挙 | 人が決めること現実的な脅威かどうかの取捨 |
| どう対処するか | Claude Codeに任せる範囲対策案の候補出し | 人が決めること軽減・受容・移転の判断 |
| できているか | Claude Codeに任せる範囲対策コードの該当行の特定 | 人が決めること検証の十分さの評価 |
準備 — 読み取り専用で始める
最初の工程はコードを読むだけで、ファイルを変更する必要がありません。Claude Codeのプランモードは、ファイルを読み、探索用のシェルコマンドを実行して計画を書きますが、承認するまでソースを編集しません。
claude --permission-mode planセッションの途中なら Shift+Tab を押して、ステータスバーに ⏸ plan mode on が出るまで切り替えます。計画が出たあとの承認画面では、Ctrl+Gで計画をエディタに開いて直接直せます。プランモード自体の詳細はPlanモード完全ガイドにあります。
探索が大きくなる場合は、探索結果を別のコンテキストに隔離できます。組み込みのExploreは読み取り専用ツールだけを持つサブエージェントで、WriteとEditが拒否されています。プランモード中にコードベースの理解が必要になると、Planサブエージェントに調査が委譲され、メインの会話は読み取り専用のままです。
手順1 — 資産と入口を棚卸しする
DFDを描く前に、何を守るかと、外から入ってくる場所を並べます。プロンプトは次のような形です。
このリポジトリの脅威モデリングの準備をします。ファイルは変更しないでください。
次の4つを、ファイルパスと行番号つきの表で出してください。
1. 外部から入る口(HTTPルート、Webhook、キューの購読、CLI引数)
2. 外部へ出る呼び出し(外部API、メール送信、ストレージ)
3. データストア(DB、キャッシュ、ファイル)とアクセス箇所
4. 認証・認可を判定している箇所
見つからなかった項目は「見つからない」と書き、推測で埋めないでください。最後の一文が効きます。ルートが動的に登録されているコードや、設定ファイルから呼び出し先が決まるコードでは、読み漏れが起きるからです。表の行数がコードの実態と合うかは、grep で自分でも数えておきます。
grep -rnE "router\.(get|post|put|delete)|app\.(get|post|put|delete)" src | wc -lClaudeが返した入口の件数とこの数が食い違えば、動的ルートか、読み漏れがあります。ここで食い違いを潰してから次へ進みます。
手順2 — 信頼境界つきのデータフロー図をMermaidで起こす
信頼境界は、「ここから先は同じ権限・同じ管理者のもとにない」という線です。ブラウザとAPIの間、APIとDBの間、自社サービスと外部APIの間が典型です。Mermaidの subgraph で境界を表すと、テキストのまま差分管理できます。
手順1の表をもとに、データフロー図をMermaidのflowchartで書いてください。
- 外部の主体、プロセス、データストアを区別する(形を変える)
- 信頼境界をsubgraphで表し、境界名をつける
- 矢印には流れるデータ(例: 注文情報、セッショントークン)をラベルにする
- 図の各要素に、根拠のファイルパスをコメントで添える架空の注文APIに対して、このプロンプトで得られる形の例です(実際の出力ではなく、構造を示す例です)。
flowchart LR
user([利用者のブラウザ]):::ext
subgraph B1[境界1: インターネット側]
user
end
subgraph B2[境界2: 自社アプリ]
api[注文API<br/>src/routes/orders.ts]
auth[認証ミドルウェア<br/>src/auth/session.ts]
end
subgraph B3[境界3: データ層]
db[(注文DB)]
end
subgraph B4[境界4: 外部サービス]
pay([決済API]):::ext
end
user -- 注文情報・セッショントークン --> auth
auth --> api
api -- 注文レコード --> db
api -- カード決済の依頼 --> pay
classDef ext fill:#eee,stroke:#888図ができたら、人が目で確かめます。見る点は2つです。矢印が実コードの呼び出しと対応しているか。境界の線が、権限や管理者が変わる場所に引かれているか。ここが間違っていると、以降の脅威表が丸ごとずれます。
手順3 — 境界を渡る矢印にSTRIDEを当てる
STRIDEは、脅威を6区分で考える枠組みです。Microsoftの脅威モデリングのドキュメントでは、次のように説明されています。
| 区分 | 意味 | 見る対象の目安 |
|---|---|---|
| Spoofing(なりすまし) | 意味他人の認証情報を不正に使う | 見る対象の目安認証・セッション・署名検証 |
| Tampering(改ざん) | 意味データの不正な変更。保存データも通信中のデータも含む | 見る対象の目安入力検証、DB更新、通信路 |
| Repudiation(否認) | 意味やっていないと言われても証明できない | 見る対象の目安監査ログ、操作の記録 |
| Information Disclosure(情報漏えい) | 意味見せてはいけない相手に情報が見える | 見る対象の目安エラー応答、ログ、アクセス制御 |
| Denial of Service(サービス拒否) | 意味正当な利用者がサービスを使えなくなる | 見る対象の目安レート制限、タイムアウト、重い処理 |
| Elevation of Privilege(権限昇格) | 意味権限のない者が高い権限を得る | 見る対象の目安認可チェック、管理用ルート |
当て方は、境界を渡る矢印を1本ずつ取り上げ、6区分を順に問う方式が確実です。全要素に全区分を当てると表が膨らむので、まず「境界を渡る矢印」と「データストア」に絞ります。OWASPのページにも、脅威・攻撃・脆弱性・影響のあらゆる組み合わせを評価しようとするのは多くの場合無駄だと書かれており、探索範囲を絞る発想と合っています。
手順2の図の、境界を渡る矢印とデータストアを対象に、STRIDEの6区分を1つずつ当ててください。
- 成り立たない区分は「該当なし」と理由を1行で書く(空欄にしない)
- 脅威は「誰が、何を、どうして」の形で書く
- 脅威ごとにID(T-01から連番)を振り、図の要素名を対応させる
- この段階ではコードを読み直さず、図と手順1の表だけを根拠にする最後の指示で、仮説の列挙と裏取りを分けます。仮説の段階でコードを読ませると、「ありそうな対策」を見つけた時点で脅威そのものを書き落とす傾向が出やすくなります。まず脅威を網羅的に挙げ、検証は別の工程にします。
手順4 — 対策済みか未対策かを該当行で裏取りする
ここが、図を描くだけの脅威モデリングとの差になります。各脅威について、対策のコードが実在するかを探させ、行番号つきで状態を付けます。
手順3の脅威表の各行について、対策がコードにあるかを調べてください。
状態は次の3つのどれかにします。
- 対策あり: 対策のコードの該当行(ファイル:行)を示す
- 対策なし: 探した場所と検索語を示す
- 判断不能: 設定やインフラ側にあって、このリポジトリからは見えない
「対策あり」は、該当行の中身を引用してください。引用できないものは「判断不能」にします。「引用できないものは判断不能」という条件が、でっち上げを抑えます。行番号だけを答えさせると、実在しない行を示される危険があります。返ってきた行は、数件を実際に開いて確かめます。
結果は次のような表になります(架空の注文APIの例です)。
| ID | 境界 | STRIDE | 脅威 | 状態と根拠 |
|---|---|---|---|---|
| T-01 | 境界境界1→2 | STRIDESpoofing | 脅威盗まれたセッショントークンで他人の注文を操作する | 状態と根拠対策あり: src/auth/session.ts:42 で有効期限を検証 |
| T-02 | 境界境界2→3 | STRIDETampering | 脅威注文IDの改ざんで他人の注文を更新する | 状態と根拠対策なし: orders.ts の更新処理に所有者チェックなし |
| T-03 | 境界境界2 | STRIDERepudiation | 脅威注文のキャンセルを否認される | 状態と根拠判断不能: ログの保管はインフラ側 |
| T-04 | 境界境界2→1 | STRIDEInformation Disclosure | 脅威エラー応答にスタックトレースが載る | 状態と根拠対策なし: 本番でも詳細を返す設定 |
区分ごとに、コードで探す対策の型はおおよそ決まっています。手順4の検索語を考えるときの手がかりとして使えます。
| 区分 | 探す対策の型 | 検索の入り口 |
|---|---|---|
| Spoofing | 探す対策の型署名・有効期限の検証 | 検索の入り口トークンの検証処理、セッション生成 |
| Tampering | 探す対策の型入力検証、所有者チェック | 検索の入り口更新系ルートの引数処理、WHERE句の条件 |
| Repudiation | 探す対策の型監査ログ | 検索の入り口操作の記録を書く箇所、ログの出力先 |
| Information Disclosure | 探す対策の型エラー応答の絞り込み、アクセス制御 | 検索の入り口例外ハンドラ、環境別の設定 |
| Denial of Service | 探す対策の型レート制限、タイムアウト | 検索の入り口ミドルウェア、外部呼び出しの設定 |
| Elevation of Privilege | 探す対策の型認可チェック | 検索の入り口管理用ルートの前段、ロール判定 |
これらは一般的な例で、どれが実際に足りているかはコードごとに違います。表のとおりに見つからなくても「対策なし」とは限らず、別の名前で実装されていることがあります。探した場所と検索語を残させるのはそのためです。
「対策なし」の行が、次の作業の入り口です。「判断不能」は放置せず、インフラ担当や設定リポジトリの持ち主に確認する宿題として残します。
手順5 — 読み取り専用のレビュー役を固定する
同じ手順を毎回プロンプトで書き直すと、観点がぶれます。サブエージェントとして定義しておくと、ツール権限も固定できます。サブエージェントの tools には使えるツールの一覧を書きます。読み取り系だけを許せば、調査中にコードを書き換えられる経路がなくなります。
---
name: threat-reviewer
description: データフロー図と脅威表の対策の有無を、コードの該当行で確認する。コードは変更しない
tools: Read, Grep, Glob
---
脅威表の各行について、対策のコードを探し、ファイル:行と該当行の引用を返す。
引用できない場合は「判断不能」とする。コードの修正案は出さない。これを .claude/agents/threat-reviewer.md に置きます。サブエージェントの仕組みはSubagents完全ガイドにまとまっています。frontmatterのフィールド名は大文字小文字が合っていないと、認識されないまま無視されます。tools のように正確に書いてください。
プロジェクトの規約は CLAUDE.md に置いておくと、毎回の指示が短くなります。
## 脅威モデリング
- 成果物は docs/threat-model.md に置く(DFDはMermaid、脅威表はT-01から連番)
- 境界を渡る矢印とデータストアを対象にし、STRIDEの6区分を全部問う
- 「対策あり」はファイル:行と引用を必ず付ける手順6 — 成果物として残し、更新のきっかけを決める
プランを承認するとプランモードを抜け、編集が始まります。ここで初めて docs/threat-model.md を書き出させます。図は文章ではなくMermaidのコードなので、レビューでは差分として読めます。
OWASPのページは、脅威モデルを更新したほうがよい契機として、新機能のリリース、セキュリティインシデントの発生、アーキテクチャやインフラの変更を挙げています。プルリクエストの説明に「境界を増やす変更か」を一言書く運用にすると、更新を忘れにくくなります。Claude Codeには、現在のブランチの変更をセキュリティ観点で見るコマンドもあります。security-reviewコマンドと、コード解析ツールとの役割の違いを整理したsecurity-reviewとCodeQLの違いが参考になります。脅威モデルは設計の層、これらは実装の層を見ます。
つまずきやすい点
- 図の粒度が細かすぎる: 関数単位まで描くと、脅威表が数百行になります。最初はサービスと外部システムの単位にとどめ、怪しい境界だけ拡大します。
- 「対策あり」の根拠が薄い: 名前が似た関数を対策と取り違えることがあります。引用つきで返させ、数件は自分で開きます。
- リポジトリ外の対策が見えない: WAFやネットワーク設定、ログ基盤はコードに出ません。「判断不能」を正直に残すほうが、誤った安心より安全です。
- 更新のたびに最初から描き直す: 2回目以降は、前回の
docs/threat-model.mdを読ませて、変更されたルートや外部呼び出しに関わる矢印だけを差分で見直させます。境界が増えていなければ、既存の脅威IDと状態はそのまま引き継げます。 - 一度で完成させようとする: 下書き、境界の修正、脅威の取捨、裏取りの順に回します。プランモードでは承認するまでファイルが変わらないので、境界の線を何度引き直しても手戻りはありません。
「対策なし」の行に優先度を付ける
脅威表は、リスクの高さの判断までは含みません。優先度は、影響の大きさと起きやすさの2軸で、人が3段階に分けます。
| 起きやすい | 起きにくい | |
|---|---|---|
| 影響が大きい(他人のデータ・決済・権限) | 起きやすい高: 次のスプリントで直す | 起きにくい中: 計画に入れて直す |
| 影響が小さい | 起きやすい中: 計画に入れて直す | 起きにくい低: 受容するか、記録だけ残す |
先ほどの例なら、T-02(他人の注文を更新できる)は影響が大きく、注文IDは推測しやすいので「高」に置く、といった具合です。T-04(スタックトレースの露出)は単体では影響が小さくても、他の脅威の足がかりになるなら一段上げます。この上げ下げの判断は、自社の資産と運用を知っている人にしかできません。
Claude Codeが出した候補を鵜呑みにせず、自社の資産と運用に照らして削る作業が、この手順で最も価値の出る部分です。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。