セキュリティの不具合は、早く見つけるほど安い——これは業界の鉄則です。本番リリース後に脆弱性が見つかれば、調査・修正・再デプロイ・場合によっては情報漏えいの対応まで、コストは青天井になります。一方、設計段階の図の上で見つければ、消しゴムで消すだけです。
この「設計段階でセキュリティを作り込む」ための体系的な手法が 脅威モデリング(Threat Modeling) です。本記事は、難解なツールの使い方ではなく、チームですぐ回せる思考の型を、公式情報(Threat Modeling Manifesto、MicrosoftのSTRIDE、OWASP Threat Modeling)に忠実に、最後は脅威を“コード”として管理する実装まで通して解説します。
この記事の位置づけ: 本記事はセキュリティエンジニアの中核技能のうち、NIST CSF 2.0でいう**「識別(Identify)」**——リスクを設計段階で洗い出す力を深掘りします。職種全体のロードマップはセキュリティエンジニアになるには、洗い出した脅威を実装で潰す技術はセキュアコーディング実践を参照してください。
0. 脅威モデリングとは — 4つの問いに答えるだけ
脅威モデリングと聞くと、専用ツールや難解な記法を想像しがちですが、本質はThreat Modeling Manifestoが掲げる4つの問いに、チームで答えることです。
- 何を作っているのか?(What are we working on?)— 対象システムを図にする。
- 何がうまくいかないか?(What can go wrong?)— 脅威を洗い出す。
- それに対して何をするのか?(What are we going to do about it?)— 緩和策を決める。
- ちゃんとやれたか?(Did we do a good enough job?)— 検証する。
この4問を、設計レビューの場で1時間回すだけでも、価値があります。Manifestoはこう言います——「完璧なモデルより、価値あるモデルを」「チェックリストより、人とコラボレーションを」。脅威モデリングは“儀式”ではなく、設計を一段深く考えるための共同作業です。
1. STRIDE — 脅威を漏れなく洗い出す6分類
「何がうまくいかないか?」を“感覚”で挙げると、必ず漏れます。そこで使うのが、Microsoftが体系化した STRIDE です。脅威を6つのカテゴリに分け、それぞれがどのセキュリティ特性を破るかと対応しています。
| STRIDE | 脅威 | 破られる特性 | 例 |
|---|---|---|---|
| Spoofing | なりすまし | 認証(Authentication) | 他人になりすましてログインする |
| Tampering | 改ざん | 完全性(Integrity) | リクエストや保存データを書き換える |
| Repudiation | 否認 | 否認防止(Non-repudiation) | 「やっていない」と言い逃れる(ログ不備) |
| Information Disclosure | 情報漏えい | 機密性(Confidentiality) | 他人のデータを盗み見る |
| Denial of Service | サービス妨害 | 可用性(Availability) | 大量リクエストでサービスを止める |
| Elevation of Privilege | 権限昇格 | 認可(Authorization) | 一般ユーザーが管理者権限を得る |
STRIDEの強みは、「この要素に、なりすましは?改ざんは?…」と6つを機械的に問えることです。網羅性が、属人的な勘を上回ります。
2. データフロー図(DFD)と信頼境界を描く
「何を作っているか」を共有する最良の道具が データフロー図(Data Flow Diagram, DFD) です。完璧な記法より、データがどこからどこへ流れ、どこで“信頼の前提”が変わるかが見えることが重要です。
最小の語彙はこれだけです。
- 外部実体(External Entity):ユーザー、外部API(こちらが制御できない)
- プロセス(Process):APIサーバー、関数(こちらのコード)
- データストア(Data Store):DB、キャッシュ、ファイル
- データフロー(Data Flow):要素間を流れるデータ(矢印)
- 信頼境界(Trust Boundary):信頼レベルが変わる線(最重要)
┌─────────────[ 信頼境界 ]─────────────┐
(境界の外=信頼できない)
[ユーザー] ──HTTPリクエスト──▶ │ [APIサーバー] ──SQL──▶ [DB]
外部実体 │ プロセス データストア
│ │
[外部決済API] ◀──HTTPS──────── │ └──ログ──▶ [ログ基盤]
外部実体 └──────────────────────────┘
信頼境界こそが脅威モデリングの心臓部です。境界を越えるデータフロー——ユーザー入力、外部APIの応答、別マイクロサービスからの呼び出し——は、**すべて「信頼できないものが内側に入る瞬間」**です。ここが、検証・認証・認可を効かせるべき場所と一致します。境界が図で見えれば、「どこを守るか」が自明になります。
3. STRIDE × DFD — 脅威を具体的に列挙する
DFDの各要素に、STRIDEの6分類を当てていきます。たとえば「ユーザー → APIサーバー」のデータフロー(境界を越える)について:
| 要素 | STRIDE | 想定される脅威 | 緩和策 |
|---|---|---|---|
| ユーザー認証 | Spoofing | 弱いパスワード/トークン窃取でなりすまし | MFA、強い認証、短命トークン |
| リクエスト本文 | Tampering | パラメータ改ざん(金額・ID) | 境界での入力検証、署名 |
| 操作の記録 | Repudiation | 「注文していない」と否認 | 改ざん耐性のある監査ログ |
| データ取得API | Information Disclosure | IDORで他人のデータ閲覧 | サーバー側の認可、RLS |
| 公開エンドポイント | Denial of Service | 大量リクエストで枯渇 | レート制限、タイムアウト |
| 権限チェック | Elevation of Privilege | 一般ユーザーが管理APIを叩く | 最小権限、サーバー側ロール検証 |
このように、「要素 × STRIDE」のマトリクスを埋めるだけで、脅威の洗い出しは驚くほど網羅的になります。すべてを完璧に潰す必要はありません。「把握できている」ことが、把握していないことより決定的に重要です。
4. 脅威モデルを“コード”として管理する
脅威モデリングが形骸化する最大の理由は、**「一度ドキュメントに書いて、そのまま腐る」**ことです。設計は変わり続けるのに、図はWikiのどこかでPNGのまま化石化する。これを防ぐ最良の方法が、脅威モデルをコードとして、リポジトリで管理することです(Threat Modeling as Code)。
まず、脅威レジストリを型のある構造化データで持ちます。
# threat-model.yaml — 脅威レジストリ(リポジトリで版管理する)
threats:
- id: T-001
component: "注文取得API"
stride: InformationDisclosure
description: "URLのIDを改ざんし、他人の注文を閲覧できる(IDOR)"
risk: high # high | medium | low(可能性 × 影響)
mitigation: "サーバー側で所有者条件をクエリに焼き込み、RLSで多層防御する"
mitigated: true # 緩和策が実装済みか
- id: T-002
component: "ログインAPI"
stride: Spoofing
description: "総当たりでパスワードを推測される"
risk: high
mitigation: "レート制限 + アカウントロック + 漏えいパスワードのブロックリスト照合"
mitigated: true
- id: T-003
component: "管理ダッシュボード"
stride: ElevationOfPrivilege
description: "一般ユーザーが管理APIを直接叩いて権限昇格する"
risk: high
mitigation: "全管理APIでサーバー側ロール検証を必須化する"
mitigated: false # ← まだ未対応
次に、**「高リスクの脅威に緩和策が無い/未実装なら、CIを落とす」**検証を書きます。これで脅威モデルが“生きた契約”になります。
// verify-threat-model.ts — 高リスクの未緩和脅威があればCIを落とす(設計と実装の乖離を防ぐ)。
import { readFileSync } from "node:fs";
import { parse } from "yaml";
import { z } from "zod";
// ① スキーマで脅威モデルを検証(型の不正・記入漏れを起動時に弾く)。
const ThreatSchema = z.object({
id: z.string().regex(/^T-\d{3}$/),
component: z.string().min(1),
stride: z.enum([
"Spoofing", "Tampering", "Repudiation",
"InformationDisclosure", "DenialOfService", "ElevationOfPrivilege",
]),
description: z.string().min(1),
risk: z.enum(["high", "medium", "low"]),
mitigation: z.string().min(1),
mitigated: z.boolean(),
});
const ModelSchema = z.object({ threats: z.array(ThreatSchema) });
function verify(path: string): number {
const model = ModelSchema.parse(parse(readFileSync(path, "utf8")));
// ② 不変条件:高リスクの脅威は、必ず緩和策が実装済みでなければならない。
const unmitigated = model.threats.filter((t) => t.risk === "high" && !t.mitigated);
if (unmitigated.length > 0) {
console.error("❌ 高リスクなのに未緩和の脅威があります:");
for (const t of unmitigated) console.error(` - ${t.id} (${t.component}): ${t.description}`);
return 1; // CIを失敗させる
}
console.log(`✅ ${model.threats.length} 件の脅威すべてに緩和策が実装済みです。`);
return 0;
}
process.exit(verify("threat-model.yaml"));
このスクリプトをCIに置けば、「設計で挙げた脅威」と「実装された緩和策」の乖離を、機械的に検知できます。脅威モデルが「腐るドキュメント」から「破れない契約」に変わる——これが、セキュリティエンジニアらしい設計の作法です。型安全な境界検証の考え方はセキュアコーディング実践に通じます。
5. 緩和策の優先順位 — すべてを守ろうとしない
脅威を100個挙げても、リソースは有限です。優先順位は リスク = 発生可能性 × 影響度 で決めます。
| 影響:大 | 影響:小 | |
|---|---|---|
| 可能性:高 | 最優先で緩和 | 早めに緩和 |
| 可能性:低 | 緩和 or 受容(要記録) | 受容(記録のみ) |
重要なのは、「受容」も正当な選択肢だということです。すべてのリスクをゼロにはできません。「このリスクは、理由があって受容している」と記録できている状態が、無自覚に放置している状態より遥かに健全です。これは経営層が責任をもつべき判断でもあり、経済産業省のサイバーセキュリティ経営ガイドラインも、リスクの把握と意思決定を経営の責務と位置づけています。
6. いつ・どのくらいやるか
脅威モデリングは「一度やって終わり」ではありません。
- 新機能の設計時:最も費用対効果が高い。設計レビューに30分〜1時間の脅威モデリングを組み込む。
- アーキテクチャ変更時:信頼境界が動くとき(新しい外部連携、認証方式の変更など)。
- 定期的に:四半期に一度など、既存モデルを見直す。
「軽く、頻繁に」が原則です。重厚な脅威モデリングを年に一度やるより、機能ごとに軽量な脅威モデリングを回し続けるほうが、変化に追従でき、チームに文化として根づきます。
7. よくある質問(FAQ)
Q. 小さなチーム/個人開発でも脅威モデリングは必要ですか? A. 必要です。むしろ少人数ほど、設計段階での手戻り防止の価値が大きい。専用ツールは要りません。ホワイトボードにDFDを描き、STRIDEの6項目を問うだけで始められます。
Q. 専用ツールは使うべきですか? A. 最初は不要です。Microsoft Threat Modeling ToolやOWASP Threat Dragonなどがありますが、本質は「4つの問い」と「STRIDE × DFD」の思考です。ツールは慣れてから。
Q. STRIDE以外の手法(PASTA、LINDDUN等)との違いは? A. STRIDEは最も学習コストが低く、汎用的です。プライバシー特化ならLINDDUN、リスク駆動ならPASTAなど目的別の手法もありますが、まずSTRIDEで体に型を入れるのが王道です。
Q. 脆弱性診断(ペネトレーションテスト)との違いは? A. 脅威モデリングは**設計段階で“予防的”に脅威を洗い出す営み、診断は実装後に“発見的”**に脆弱性を探す営みです。両輪です。診断のやり方はWebアプリ脆弱性診断、攻撃者視点はホワイトハッカー入門を参照してください。
Q. 開発が遅くなりませんか? A. 短期的には設計に時間が増えます。しかし、設計段階で潰した脅威は、リリース後のインシデント対応コスト(NIST 800-61に沿った対応)と比べれば桁違いに安い。むしろ全体の開発速度は上がります。
8. まとめ
脅威モデリングは、特別な才能や高価なツールではなく、設計を一段深く考えるための共同作業です。
- 4つの問いを回す。 何を作り/何がうまくいかず/何をして/ちゃんとやれたか。
- STRIDE × DFDで網羅する。 信頼境界を描き、各要素に6分類を当てる。勘を網羅性で上回る。
- コードとして管理する。 脅威レジストリをYAMLで持ち、緩和策の有無をCIで検証し、設計と実装の乖離を防ぐ。
- 優先順位をつけ、受容も記録する。 すべては守れない。把握していることが、放置より遥かに健全。
- 軽く、頻繁に。 機能ごとに回し続け、文化として根づかせる。
設計段階でセキュリティを作り込めるかどうかは、プロダクトの安全性を最も大きく左右します。「速く作る」ことと「安全に作る」ことは、設計の最初の30分で両立できる——それが脅威モデリングの本質です。あなたのプロダクトの設計レビューに脅威モデリングを組み込みたい、あるいは既存設計の“縦のリスク”を一度棚卸ししたい場合は、お気軽にご相談ください。