[Threat Thinker] LLMを用いた脅威モデリングを試してみる
はじめに
こんにちは、セキュリティエンジニアのJJ(yuasa)です。
本記事では、LLMを用いた自動脅威モデリングツールであるThreat Thinkerを用いて様々なシステムの脅威モデリングを試してみます。AWSベースシステムからスマートホームまで、LLMがどのように脅威を洗い出すのか、実際の出力を交えながらLLM脅威モデリングの雰囲気を体験していただければ幸いです。
Threat Thinkerとは?
Threat Thinker は、LLMを用いてシステム構成図から自動で脅威モデリングを行うツールです。Mermaidやdraw.io、構成図のスクリーンショット、OWASP Threat Dragonなど、さまざまな形式の図を入力として解析し、コンポーネント間の関係から脅威を推論します。
従来の脅威モデリングでは、構成図を用意したあとに、開発者やセキュリティ担当者が手作業で脅威を一つひとつ検討する必要がありました。その中には、どのシステムでも発生し得る「基本的な脅威」と、仕様や実装の詳細を理解しないと気づけない「システム固有の脅威」があるように思います。
Threat Thinkerを使うことで、まずは「基本的な脅威」の洗い出しを自動化でき、人間は「システム固有の脅威」の深掘りや対策検討といった部分に注力できるようになります。CLIとWeb UIの両方に対応しており、セキュリティ専門家でなくても気軽に利用できる点も特徴です。

システム構成図から基本的な脅威を自動で洗い出し
Threat Thinkerを使ってみる
Threat Thinkerを用いて、AWSベースシステム、企業ネットワーク、スマートホームの脅威を洗い出してみます。ここでは、それぞれの構成図をThreat Thinkerに入力し、どのような脅威が抽出されるのかを確認していきます。
AWSベースシステム
システム構成図
対象となるAWSベースシステムの構成図はこちらです。Mermaidで作成しています。Webアプリケーションでよく利用される CloudFront → ALB → ECS → RDS/S3 という比較的シンプルな構成を用いて、Threat Thinker がどのように脅威を抽出するのかを確認します。
Threat Thinkerの実行
ここではCLIでの実行例を示します。--diagramでMermaid形式の構成図へのファイルパスを指定します。--infer-hintsを付けることで、LLMが図から読み取れない補助的な情報も推論した上で脅威を推論してくれます。LLMのモデルはOpenAIのgpt-4.1を使用し、最大5件の脅威を出力するように指定しています。
threat-thinker think \
--diagram path/to/diagram/system.mmd \
--infer-hints \
--topn 5 \
--llm-api openai \
--llm-model gpt-4.1 \
--out-dir path/to/report/dir
洗い出された脅威
Markdownレポートで示される上位5件の脅威は以下の通りです。いずれもAWSを用いてWebアプリケーションを構成する際に典型的に発生し得るリスクで、認証・認可の不備、データ暗号化の不備、ロギング・監視の不足、S3の設定ミスといった基本的な観点が指摘されています。
Markdownレポート
Threat Analysis Report
Threat Summary
| ID | Threat | Severity | Score |
|---|---|---|---|
| T001 | Potential Lack of Authentication/Authorization on ALB to ECS Path | High | 8.0 |
| T002 | Unencrypted Traffic Between ALB and ECS Allows Tampering and Disclosure | High | 8.0 |
| T003 | Exposure of PII in RDS Without Explicit Encryption at Rest | High | 7.0 |
| T004 | Insufficient Logging and Monitoring for Sensitive Operations | Medium | 6.0 |
| T005 | Potential S3 Bucket Misconfiguration Exposing Internal Data | Medium | 6.0 |
Threat Details
T001: Potential Lack of Authentication/Authorization on ALB to ECS Path
Severity: High
Score: 8.0
STRIDE: Elevation of Privilege, Spoofing
Affected Components: ALB, ECS Service
Why: No explicit authentication or authorization is described between ALB and ECS, risking unauthorized access to internal APIs.
References: ASVS V2.1, ASVS V4.2, CWE-285
Recommended Actions:
Require strong authentication (e.g., JWT, OAuth 2.0) and enforce authorization checks on ECS endpoints.
T002: Unencrypted Traffic Between ALB and ECS Allows Tampering and Disclosure
Severity: High
Score: 8.0
STRIDE: Tampering, Information Disclosure
Affected Components: ALB, ECS Service
Why: HTTP is used between ALB and ECS, exposing internal traffic to interception or modification if the subnet is compromised.
References: ASVS V9.1.1, ASVS V9.2.1, CWE-319
Recommended Actions:
Enforce HTTPS/TLS 1.2+ for all traffic between ALB and ECS to ensure confidentiality and integrity.
T003: Exposure of PII in RDS Without Explicit Encryption at Rest
Severity: High
Score: 7.0
STRIDE: Information Disclosure
Affected Components: ECS Service, Customer RDS
PII
Why: RDS contains PII but there is no mention of encryption at rest, risking data exposure if storage is compromised.
References: ASVS V9.4.1, ASVS V9.4.2, CWE-311
Recommended Actions:
Enable RDS encryption at rest and enforce encrypted backups for all PII-containing databases.
T004: Insufficient Logging and Monitoring for Sensitive Operations
Severity: Medium
Score: 6.0
STRIDE: Repudiation
Affected Components: ECS Service, Customer RDS
PII, S3 Bucket
Logs/Uploads
Why: No evidence of audit logging for access to PII or uploads, making it hard to detect or investigate misuse.
References: ASVS V10.1, ASVS V10.2, CWE-778
Recommended Actions:
Implement detailed audit logging for all access to RDS and S3, and monitor logs for suspicious activity.
T005: Potential S3 Bucket Misconfiguration Exposing Internal Data
Severity: Medium
Score: 6.0
STRIDE: Information Disclosure
Affected Components: S3 Bucket
Logs/Uploads
Why: S3 bucket is used for internal data, but no mention of bucket policy or access controls, risking accidental public exposure.
References: ASVS V9.1.1, ASVS V9.4.3, CWE-200
Recommended Actions:
Restrict S3 bucket access to only necessary IAM roles and enable bucket policy to deny public access.
| ID | Threat | Severity | Score |
|---|---|---|---|
| T001 | Potential Lack of Authentication/Authorization on ALB to ECS Path | High | 8.0 |
| T002 | Unencrypted Traffic Between ALB and ECS Allows Tampering and Disclosure | High | 8.0 |
| T003 | Exposure of PII in RDS Without Explicit Encryption at Rest | High | 7.0 |
| T004 | Insufficient Logging and Monitoring for Sensitive Operations | Medium | 6.0 |
| T005 | Potential S3 Bucket Misconfiguration Exposing Internal Data | Medium | 6.0 |
HTMLレポート上では、それぞれの脅威がシステム構成図のどこにあるのかを視覚的に把握することができます。
HTMLレポート
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Threat Analysis Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 32px; color: #0f172a; }
h1 { font-size: 28px; margin-bottom: 8px; }
h2 { margin-top: 32px; border-bottom: 2px solid #e2e8f0; padding-bottom: 4px; }
h3 { margin-top: 24px; color: #0f172a; }
table { border-collapse: collapse; width: 100%; margin-top: 12px; }
th, td { border: 1px solid #e2e8f0; padding: 8px 10px; text-align: left; }
th { background: #f8fafc; }
.severity { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 12px; font-weight: 600; }
.sev-High { background: #fee2e2; color: #b91c1c; }
.sev-Medium { background: #fef9c3; color: #b45309; }
.sev-Low { background: #dcfce7; color: #166534; }
.meta { color: #475569; font-size: 14px; }
.section { margin-top: 24px; }
.mapping-list { list-style: disc; margin-left: 20px; }
.chip { background: #e2e8f0; padding: 2px 6px; border-radius: 8px; margin-right: 4px; display: inline-block; cursor: pointer; }
#graph-container { margin-top: 24px; }
#graph { width: 100%; height: 520px; border: 1px solid #e2e8f0; border-radius: 8px; }
.dom-highlight { box-shadow: 0 0 0 2px #f97316; }
.cy-highlight { }
.cy-dim { }
.zone { background-color: #f8fafc; border: 1px dashed #cbd5e1; padding: 8px; }
</style>
</head>
<body>
<h1>Threat Analysis Report</h1>
<h2>Threat Summary</h2>
<table>
<tr><th>ID</th><th>Threat</th><th>Severity</th><th>Score</th></tr>
<tr class="threat-row" data-threat-id="T001"><td>T001</td><td>Potential Lack of Authentication/Authorization on ALB to ECS Path</td><td><span class="severity sev-High">High</span></td><td>8.0</td></tr>
<tr class="threat-row" data-threat-id="T002"><td>T002</td><td>Unencrypted Traffic Between ALB and ECS Allows Tampering and Disclosure</td><td><span class="severity sev-High">High</span></td><td>8.0</td></tr>
<tr class="threat-row" data-threat-id="T003"><td>T003</td><td>Exposure of PII in RDS Without Explicit Encryption at Rest</td><td><span class="severity sev-High">High</span></td><td>7.0</td></tr>
<tr class="threat-row" data-threat-id="T004"><td>T004</td><td>Insufficient Logging and Monitoring for Sensitive Operations</td><td><span class="severity sev-Medium">Medium</span></td><td>6.0</td></tr>
<tr class="threat-row" data-threat-id="T005"><td>T005</td><td>Potential S3 Bucket Misconfiguration Exposing Internal Data</td><td><span class="severity sev-Medium">Medium</span></td><td>6.0</td></tr>
</table>
<div id="graph-container">
<h2>Architecture Graph</h2>
<div id="graph"></div>
</div>
<h2>Architecture Mapping</h2>
<h3>Nodes to Threats</h3>
<table>
<tr><th>Node</th><th>Zone</th><th>Type</th><th>Threats</th></tr>
<tr><td>User [user]</td><td>Internet</td><td>actor</td><td>—</td></tr>
<tr><td>CloudFront [cf]</td><td>Edge</td><td>service</td><td>—</td></tr>
<tr><td>ALB [alb]</td><td>VPC > Public subnet</td><td>elb</td><td><span class="chip" data-threat-id="T001">T001</span> <span class="chip" data-threat-id="T002">T002</span></td></tr>
<tr><td>ECS Service [ecs]</td><td>VPC > Private subnet</td><td>service</td><td><span class="chip" data-threat-id="T001">T001</span> <span class="chip" data-threat-id="T002">T002</span> <span class="chip" data-threat-id="T004">T004</span></td></tr>
<tr><td>Customer RDS<br>PII [rds]</td><td>VPC > Private subnet</td><td>database</td><td><span class="chip" data-threat-id="T003">T003</span> <span class="chip" data-threat-id="T004">T004</span></td></tr>
<tr><td>S3 Bucket<br>Logs/Uploads [s3]</td><td>VPC > Private subnet</td><td>s3</td><td><span class="chip" data-threat-id="T004">T004</span> <span class="chip" data-threat-id="T005">T005</span></td></tr>
</table>
<h3>Edges to Threats</h3>
<table>
<tr><th>Edge</th><th>Protocol</th><th>Threats</th></tr>
<tr><td>User [user] -> CloudFront [cf] : sends HTTPS request</td><td>HTTPS</td><td>—</td></tr>
<tr><td>CloudFront [cf] -> ALB [alb] : forwards HTTPS request</td><td>HTTPS</td><td>—</td></tr>
<tr><td>ALB [alb] -> ECS Service [ecs] : routes HTTP request</td><td>HTTP</td><td><span class="chip" data-threat-id="T001">T001</span> <span class="chip" data-threat-id="T002">T002</span></td></tr>
<tr><td>ECS Service [ecs] -> Customer RDS<br>PII [rds] : reads/writes data (SQL/TLS)</td><td>TCP</td><td><span class="chip" data-threat-id="T003">T003</span> <span class="chip" data-threat-id="T004">T004</span></td></tr>
<tr><td>ECS Service [ecs] -> S3 Bucket<br>Logs/Uploads [s3] : stores/reads objects (S3 API)</td><td>HTTPS</td><td><span class="chip" data-threat-id="T004">T004</span> <span class="chip" data-threat-id="T005">T005</span></td></tr>
</table>
<h2>Threat Details</h2>
<div class="section" id="T001" data-threat-id="T001">
<h3>T001: Potential Lack of Authentication/Authorization on ALB to ECS Path</h3>
<div class="meta">
Severity: <span class="severity sev-High">High</span> | Score: 8.0 | STRIDE: Elevation of Privilege, Spoofing
</div>
<p><strong>Affected Components:</strong> ALB, ECS Service</p>
<p><strong>Why:</strong> No explicit authentication or authorization is described between ALB and ECS, risking unauthorized access to internal APIs.</p>
<p><strong>References:</strong> ASVS V2.1, ASVS V4.2, CWE-285</p>
<p><strong>Recommended Actions:</strong><br>Require strong authentication (e.g., JWT, OAuth 2.0) and enforce authorization checks on ECS endpoints.</p>
<div class="section">
<h4>Evidence Mapping</h4>
<p><em>Nodes:</em></p>
<ul class="mapping-list">
<li>ALB [alb] (zone=VPC > Public subnet, type=elb)</li>
<li>ECS Service [ecs] (zone=VPC > Private subnet, type=service)</li>
</ul>
<p><em>Edges:</em></p>
<ul class="mapping-list">
<li>ALB [alb] -> ECS Service [ecs] : routes HTTP request (HTTP)</li>
</ul>
</div>
</div>
<div class="section" id="T002" data-threat-id="T002">
<h3>T002: Unencrypted Traffic Between ALB and ECS Allows Tampering and Disclosure</h3>
<div class="meta">
Severity: <span class="severity sev-High">High</span> | Score: 8.0 | STRIDE: Tampering, Information Disclosure
</div>
<p><strong>Affected Components:</strong> ALB, ECS Service</p>
<p><strong>Why:</strong> HTTP is used between ALB and ECS, exposing internal traffic to interception or modification if the subnet is compromised.</p>
<p><strong>References:</strong> ASVS V9.1.1, ASVS V9.2.1, CWE-319</p>
<p><strong>Recommended Actions:</strong><br>Enforce HTTPS/TLS 1.2+ for all traffic between ALB and ECS to ensure confidentiality and integrity.</p>
<div class="section">
<h4>Evidence Mapping</h4>
<p><em>Nodes:</em></p>
<ul class="mapping-list">
<li>ALB [alb] (zone=VPC > Public subnet, type=elb)</li>
<li>ECS Service [ecs] (zone=VPC > Private subnet, type=service)</li>
</ul>
<p><em>Edges:</em></p>
<ul class="mapping-list">
<li>ALB [alb] -> ECS Service [ecs] : routes HTTP request (HTTP)</li>
</ul>
</div>
</div>
<div class="section" id="T003" data-threat-id="T003">
<h3>T003: Exposure of PII in RDS Without Explicit Encryption at Rest</h3>
<div class="meta">
Severity: <span class="severity sev-High">High</span> | Score: 7.0 | STRIDE: Information Disclosure
</div>
<p><strong>Affected Components:</strong> ECS Service, Customer RDS<br>PII</p>
<p><strong>Why:</strong> RDS contains PII but there is no mention of encryption at rest, risking data exposure if storage is compromised.</p>
<p><strong>References:</strong> ASVS V9.4.1, ASVS V9.4.2, CWE-311</p>
<p><strong>Recommended Actions:</strong><br>Enable RDS encryption at rest and enforce encrypted backups for all PII-containing databases.</p>
<div class="section">
<h4>Evidence Mapping</h4>
<p><em>Nodes:</em></p>
<ul class="mapping-list">
<li>Customer RDS<br>PII [rds] (zone=VPC > Private subnet, type=database)</li>
</ul>
<p><em>Edges:</em></p>
<ul class="mapping-list">
<li>ECS Service [ecs] -> Customer RDS<br>PII [rds] : reads/writes data (SQL/TLS) (TCP)</li>
</ul>
</div>
</div>
<div class="section" id="T004" data-threat-id="T004">
<h3>T004: Insufficient Logging and Monitoring for Sensitive Operations</h3>
<div class="meta">
Severity: <span class="severity sev-Medium">Medium</span> | Score: 6.0 | STRIDE: Repudiation
</div>
<p><strong>Affected Components:</strong> ECS Service, Customer RDS<br>PII, S3 Bucket<br>Logs/Uploads</p>
<p><strong>Why:</strong> No evidence of audit logging for access to PII or uploads, making it hard to detect or investigate misuse.</p>
<p><strong>References:</strong> ASVS V10.1, ASVS V10.2, CWE-778</p>
<p><strong>Recommended Actions:</strong><br>Implement detailed audit logging for all access to RDS and S3, and monitor logs for suspicious activity.</p>
<div class="section">
<h4>Evidence Mapping</h4>
<p><em>Nodes:</em></p>
<ul class="mapping-list">
<li>ECS Service [ecs] (zone=VPC > Private subnet, type=service)</li>
<li>Customer RDS<br>PII [rds] (zone=VPC > Private subnet, type=database)</li>
<li>S3 Bucket<br>Logs/Uploads [s3] (zone=VPC > Private subnet, type=s3)</li>
</ul>
<p><em>Edges:</em></p>
<ul class="mapping-list">
<li>ECS Service [ecs] -> Customer RDS<br>PII [rds] : reads/writes data (SQL/TLS) (TCP)</li>
<li>ECS Service [ecs] -> S3 Bucket<br>Logs/Uploads [s3] : stores/reads objects (S3 API) (HTTPS)</li>
</ul>
</div>
</div>
<div class="section" id="T005" data-threat-id="T005">
<h3>T005: Potential S3 Bucket Misconfiguration Exposing Internal Data</h3>
<div class="meta">
Severity: <span class="severity sev-Medium">Medium</span> | Score: 6.0 | STRIDE: Information Disclosure
</div>
<p><strong>Affected Components:</strong> S3 Bucket<br>Logs/Uploads</p>
<p><strong>Why:</strong> S3 bucket is used for internal data, but no mention of bucket policy or access controls, risking accidental public exposure.</p>
<p><strong>References:</strong> ASVS V9.1.1, ASVS V9.4.3, CWE-200</p>
<p><strong>Recommended Actions:</strong><br>Restrict S3 bucket access to only necessary IAM roles and enable bucket policy to deny public access.</p>
<div class="section">
<h4>Evidence Mapping</h4>
<p><em>Nodes:</em></p>
<ul class="mapping-list">
<li>S3 Bucket<br>Logs/Uploads [s3] (zone=VPC > Private subnet, type=s3)</li>
</ul>
<p><em>Edges:</em></p>
<ul class="mapping-list">
<li>ECS Service [ecs] -> S3 Bucket<br>Logs/Uploads [s3] : stores/reads objects (S3 API) (HTTPS)</li>
</ul>
</div>
</div>
<script>
window.THREAT_REPORT = {"graph": {"nodes": [{"id": "user", "label": "User", "zone": "Internet", "zones": ["zone_0"], "zone_path": ["Internet"], "type": "actor", "data": [], "auth": null, "notes": null}, {"id": "cf", "label": "CloudFront", "zone": "Edge", "zones": ["zone_1"], "zone_path": ["Edge"], "type": "service", "data": [], "auth": null, "notes": null}, {"id": "alb", "label": "ALB", "zone": "Public subnet", "zones": ["zone_2", "zone_3"], "zone_path": ["VPC", "Public subnet"], "type": "elb", "data": [], "auth": null, "notes": null}, {"id": "ecs", "label": "ECS Service", "zone": "Private subnet", "zones": ["zone_2", "zone_4"], "zone_path": ["VPC", "Private subnet"], "type": "service", "data": ["Internal"], "auth": true, "notes": null}, {"id": "rds", "label": "Customer RDS<br>PII", "zone": "Private subnet", "zones": ["zone_2", "zone_4"], "zone_path": ["VPC", "Private subnet"], "type": "database", "data": ["PII"], "auth": true, "notes": null}, {"id": "s3", "label": "S3 Bucket<br>Logs/Uploads", "zone": "Private subnet", "zones": ["zone_2", "zone_4"], "zone_path": ["VPC", "Private subnet"], "type": "s3", "data": ["Internal"], "auth": true, "notes": null}], "edges": [{"id": "user->cf", "src": "user", "dst": "cf", "label": "sends HTTPS request", "protocol": "HTTPS", "data": []}, {"id": "cf->alb", "src": "cf", "dst": "alb", "label": "forwards HTTPS request", "protocol": "HTTPS", "data": []}, {"id": "alb->ecs", "src": "alb", "dst": "ecs", "label": "routes HTTP request", "protocol": "HTTP", "data": []}, {"id": "ecs->rds", "src": "ecs", "dst": "rds", "label": "reads/writes data (SQL/TLS)", "protocol": "TCP", "data": ["PII"]}, {"id": "ecs->s3", "src": "ecs", "dst": "s3", "label": "stores/reads objects (S3 API)", "protocol": "HTTPS", "data": ["Internal"]}], "zones": [{"id": "zone_0", "name": "Internet", "parent_id": null}, {"id": "zone_1", "name": "Edge", "parent_id": null}, {"id": "zone_2", "name": "VPC", "parent_id": null}, {"id": "zone_3", "name": "Public subnet", "parent_id": "zone_2"}, {"id": "zone_4", "name": "Private subnet", "parent_id": "zone_2"}]}, "threats": [{"id": "T001", "title": "Potential Lack of Authentication/Authorization on ALB to ECS Path", "severity": "High", "score": 8.0, "stride": ["Elevation of Privilege", "Spoofing"], "affected": ["ALB", "ECS Service"], "why": "No explicit authentication or authorization is described between ALB and ECS, risking unauthorized access to internal APIs.", "references": ["ASVS V2.1", "ASVS V4.2", "CWE-285"], "recommended_action": "Require strong authentication (e.g., JWT, OAuth 2.0) and enforce authorization checks on ECS endpoints.", "evidence": {"nodes": ["alb", "ecs"], "edges": ["alb->ecs"]}}, {"id": "T002", "title": "Unencrypted Traffic Between ALB and ECS Allows Tampering and Disclosure", "severity": "High", "score": 8.0, "stride": ["Tampering", "Information Disclosure"], "affected": ["ALB", "ECS Service"], "why": "HTTP is used between ALB and ECS, exposing internal traffic to interception or modification if the subnet is compromised.", "references": ["ASVS V9.1.1", "ASVS V9.2.1", "CWE-319"], "recommended_action": "Enforce HTTPS/TLS 1.2+ for all traffic between ALB and ECS to ensure confidentiality and integrity.", "evidence": {"nodes": ["alb", "ecs"], "edges": ["alb->ecs"]}}, {"id": "T003", "title": "Exposure of PII in RDS Without Explicit Encryption at Rest", "severity": "High", "score": 7.0, "stride": ["Information Disclosure"], "affected": ["ECS Service", "Customer RDS<br>PII"], "why": "RDS contains PII but there is no mention of encryption at rest, risking data exposure if storage is compromised.", "references": ["ASVS V9.4.1", "ASVS V9.4.2", "CWE-311"], "recommended_action": "Enable RDS encryption at rest and enforce encrypted backups for all PII-containing databases.", "evidence": {"nodes": ["rds"], "edges": ["ecs->rds"]}}, {"id": "T004", "title": "Insufficient Logging and Monitoring for Sensitive Operations", "severity": "Medium", "score": 6.0, "stride": ["Repudiation"], "affected": ["ECS Service", "Customer RDS<br>PII", "S3 Bucket<br>Logs/Uploads"], "why": "No evidence of audit logging for access to PII or uploads, making it hard to detect or investigate misuse.", "references": ["ASVS V10.1", "ASVS V10.2", "CWE-778"], "recommended_action": "Implement detailed audit logging for all access to RDS and S3, and monitor logs for suspicious activity.", "evidence": {"nodes": ["ecs", "rds", "s3"], "edges": ["ecs->rds", "ecs->s3"]}}, {"id": "T005", "title": "Potential S3 Bucket Misconfiguration Exposing Internal Data", "severity": "Medium", "score": 6.0, "stride": ["Information Disclosure"], "affected": ["S3 Bucket<br>Logs/Uploads"], "why": "S3 bucket is used for internal data, but no mention of bucket policy or access controls, risking accidental public exposure.", "references": ["ASVS V9.1.1", "ASVS V9.4.3", "CWE-200"], "recommended_action": "Restrict S3 bucket access to only necessary IAM roles and enable bucket policy to deny public access.", "evidence": {"nodes": ["s3"], "edges": ["ecs->s3"]}}]};
</script>
<script src="https://unpkg.com/cytoscape@3.33.1/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/dagre@0.8.5/dist/dagre.min.js"></script>
<script src="https://unpkg.com/cytoscape-dagre@2.5.0/cytoscape-dagre.js"></script>
<script>
(function() {
const report = window.THREAT_REPORT || {};
const nodes = (report.graph && report.graph.nodes) || [];
const edges = (report.graph && report.graph.edges) || [];
const container = document.getElementById('graph');
if (!container) return;
let initialZoom = 1;
const cssEscape = (value) => (window.CSS && CSS.escape ? CSS.escape(value) : value);
const edgeIdMap = new Map();
edges.forEach((e) => {
const primary = e.id || `${e.src}->${e.dst}`;
const aliases = new Set([primary, `${e.src}->${e.dst}`]);
if (e.label) aliases.add(`${e.src}->${e.dst}:${e.label}`);
aliases.forEach((alias) => edgeIdMap.set(alias, primary));
});
function clearCyHighlight(cy) {
cy.elements().removeClass('cy-highlight cy-dim');
}
function highlightThreat(cy, reportObj, threatId) {
if (!threatId) return;
const threat = (reportObj.threats || []).find((t) => t.id === threatId);
clearCyHighlight(cy);
if (!threat) return;
const nodeIds = (threat.evidence && threat.evidence.nodes) || [];
const edgeIds = ((threat.evidence && threat.evidence.edges) || []).map((id) => edgeIdMap.get(id) || id);
const threatNodes = cy.nodes().filter((n) => nodeIds.includes(n.id()));
const threatEdges = cy.edges().filter((e) => edgeIds.includes(e.id()));
const targets = threatNodes.union(threatEdges);
if (targets.length) {
targets.addClass('cy-highlight');
cy.elements().difference(targets).addClass('cy-dim');
try {
cy.fit(targets, 60);
const bbox = targets.boundingBox();
const center = { x: (bbox.x1 + bbox.x2) / 2, y: (bbox.y1 + bbox.y2) / 2 };
const clampedZoom = Math.min(cy.zoom(), initialZoom * 1.2);
if (cy.zoom() > clampedZoom) {
cy.zoom({ level: clampedZoom, position: center });
}
} catch (err) {}
}
}
function clearDomHighlights() {
document.querySelectorAll('.dom-highlight').forEach((el) => el.classList.remove('dom-highlight'));
}
function scrollToThreat(threatId) {
const detail = document.getElementById(threatId);
if (detail && typeof detail.scrollIntoView === 'function') {
detail.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}
function highlightRows(threatIds) {
clearDomHighlights();
threatIds.forEach((tid) => {
const row = document.querySelector(`.threat-row[data-threat-id="${cssEscape(tid)}"]`);
if (row) row.classList.add('dom-highlight');
});
}
function bindInteractions(cy) {
const rows = Array.from(document.querySelectorAll('.threat-row[data-threat-id]'));
rows.forEach((row) => {
row.addEventListener('click', () => {
const tid = row.getAttribute('data-threat-id');
highlightRows([tid]);
highlightThreat(cy, report, tid);
});
});
const chips = Array.from(document.querySelectorAll('.chip[data-threat-id]'));
chips.forEach((chip) => {
chip.addEventListener('click', () => {
const tid = chip.getAttribute('data-threat-id');
highlightRows([tid]);
highlightThreat(cy, report, tid);
});
});
cy.on('tap', 'node, edge', (evt) => {
const elementId = evt.target.id();
const matchingThreats = (report.threats || []).filter((t) => {
const ev = t.evidence || {};
const nids = ev.nodes || [];
const eids = ev.edges || [];
return nids.includes(elementId) || eids.includes(elementId);
});
const ids = matchingThreats.map((t) => t.id);
highlightRows(ids);
clearCyHighlight(cy);
evt.target.addClass('cy-highlight');
cy.elements().difference(evt.target).addClass('cy-dim');
});
}
function createCy() {
const palette = ['#0ea5e9','#22c55e','#f97316','#a78bfa','#f43f5e','#14b8a6','#eab308','#3b82f6'];
const zoneColor = {};
const graphZones = (report.graph && report.graph.zones) || [];
const zoneElements = graphZones.length
? graphZones.map((z) => {
const label = z.name || z.id;
if (label && !zoneColor[label]) {
zoneColor[label] = palette[Object.keys(zoneColor).length % palette.length];
}
const zoneId = `zone::${String(z.id).replace(/\s+/g, '_')}`;
const parent = z.parent_id ? `zone::${String(z.parent_id).replace(/\s+/g, '_')}` : undefined;
return { data: { id: zoneId, label: label, type: 'zone', parent } };
})
: Array.from(new Set(nodes.map((n) => (n.zone_path && n.zone_path.length ? n.zone_path[n.zone_path.length - 1] : n.zone)).filter(Boolean))).map((label) => {
if (!zoneColor[label]) {
zoneColor[label] = palette[Object.keys(zoneColor).length % palette.length];
}
const zoneId = `zone::${String(label).replace(/\s+/g, '_')}`;
return { data: { id: zoneId, label, type: 'zone' } };
});
const elements = {
nodes: [
...zoneElements,
...nodes.map((n) => {
const zoneId = (n.zones && n.zones.length) ? n.zones[n.zones.length - 1] : null;
const zoneLabel = (n.zone_path && n.zone_path.length) ? n.zone_path[n.zone_path.length - 1] : (n.zone || zoneId || 'default');
if (zoneLabel && !zoneColor[zoneLabel]) {
zoneColor[zoneLabel] = palette[Object.keys(zoneColor).length % palette.length];
}
const color = zoneColor[zoneLabel] || '#0ea5e9';
const parent = zoneId ? `zone::${String(zoneId).replace(/\s+/g, '_')}` : undefined;
return { data: { id: n.id, label: n.label, zone: zoneLabel, type: n.type, color, parent } };
})
],
edges: edges.map((e) => {
const edgeId = e.id || `${e.src}->${e.dst}`;
return { data: { id: edgeId, source: e.src, target: e.dst, label: e.label || '', protocol: e.protocol || '' } };
})
};
const cy = cytoscape({
container,
elements,
style: [
{ selector: 'node[type = \"zone\"]', style: { 'background-color': '#f8fafc', 'background-opacity': 0.25, 'shape': 'round-rectangle', 'label': 'data(label)', 'color': '#0f172a', 'text-valign': 'top', 'text-halign': 'center', 'text-wrap': 'wrap', 'font-weight': 700, 'font-size': 12, 'text-background-color': '#f8fafc', 'text-background-opacity': 0.9, 'text-background-padding': 4, 'text-margin-y': -12, 'border-style': 'dashed', 'border-color': '#94a3b8', 'border-width': 2, 'padding': 18, 'z-compound-depth': 'bottom' } },
{ selector: 'node', style: { 'background-color': 'data(color)', 'label': 'data(label)', 'color': '#0f172a', 'text-valign': 'center', 'text-halign': 'center', 'text-wrap': 'wrap', 'font-size': 10, 'border-width': 1, 'border-color': '#0f172a10', 'z-compound-depth': 'top' } },
{ selector: 'edge', style: { 'curve-style': 'bezier', 'target-arrow-shape': 'triangle', 'width': 2, 'line-color': '#94a3b8', 'target-arrow-color': '#94a3b8', 'label': 'data(label)', 'font-size': 8, 'text-background-color': '#fff', 'text-background-opacity': 0.7, 'text-background-padding': 2 } },
{ selector: 'node.cy-highlight', style: { 'background-color': '#f97316', 'border-color': '#f97316', 'border-width': 3 } },
{ selector: 'edge.cy-highlight', style: { 'line-color': '#f97316', 'target-arrow-color': '#f97316', 'width': 3 } },
{ selector: '.cy-dim', style: { 'opacity': 0.25 } }
],
});
try {
cy.layout({ name: 'dagre', rankDir: 'LR', padding: 30, nodeSep: 40, edgeSep: 20 }).run();
} catch (e) {
cy.layout({ name: 'cose', padding: 30, animate: false }).run();
}
cy.nodes().forEach((n) => n.grabbable(true));
initialZoom = cy.zoom();
return cy;
}
const cy = createCy();
bindInteractions(cy);
})();
</script>
</body>
</html>

抽出されたシステム構成図

脅威がシステム構成図のどこにあるのかを表示
企業ネットワーク
システム構成図
対象となる企業ネットワークの構成図はこちらです。draw.ioで作成しています。インターネット、DMZ、社内ネットワークの3つに分かれた、中小企業向けのシンプルなネットワーク構成となっています。

企業ネットワークのシステム構成図
Threat Thinkerの実行
ここではWeb UIを用いて実行してみます。Web UIはthreat-thinker webuiで起動することができます。
$ threat-thinker webui
ℹ️ Starting Threat Thinker Web UI
* Running on local URL: http://127.0.0.1:7860
* To create a public link, set `share=True` in `launch()`.
draw.io(XML)形式のシステム構成図をコピーして貼り付け、Diagram Formatはdrawioを指定します。

諸々のオプションを指定して、Generate Reportをクリックします。

洗い出された脅威
Markdownレポートで示される上位5件の脅威は以下の通りです。外部からアクセス可能な Webサーバに対する攻撃のリスク、入力バリデーション不備によるインジェクション脆弱性、DMZから社内ネットワークへの横展開、内部サーバに保存された機密データの露出リスクなどが含まれています。企業ネットワークで典型的に想定されるリスクがバランスよく指摘されています。
Markdownレポート
Threat Analysis Report
Threat Summary
| ID | Threat | Severity | Score |
|---|---|---|---|
| T001 | External Attackers Can Reach Public-Facing Web Server | High | 9.0 |
| T002 | Insufficient Input Validation on Public Web Server | High | 8.0 |
| T003 | Potential Lateral Movement from DMZ to Internal Network | High | 8.0 |
| T004 | Sensitive Data Exposure on File and Directory Servers | High | 8.0 |
| T005 | VPN Gateway Exposed to Credential Attacks | High | 8.0 |
Threat Details
T001: External Attackers Can Reach Public-Facing Web Server
Severity: High
Score: 9.0
STRIDE: Spoofing, Tampering, Information Disclosure, Denial of Service
Affected Components: Internet, Web Server
Why: Web Server (node 5) is accessible from the Internet (node 2) via External Firewall (node 3), exposing it to direct attacks.
References: ASVS V1.1, ASVS V10.1, CWE-20
Recommended Actions:
Harden the Web Server, apply latest security patches, enforce input validation, and deploy a Web Application Firewall (WAF).
T002: Insufficient Input Validation on Public Web Server
Severity: High
Score: 8.0
STRIDE: Tampering, Information Disclosure, Elevation of Privilege
Affected Components: Web Server
Why: Web Server (node 5) processes user input from the Internet, risking injection and XSS if input is not validated.
References: ASVS V5.1, ASVS V5.3, CWE-20
Recommended Actions:
Implement strict server-side input validation and output encoding.
T003: Potential Lateral Movement from DMZ to Internal Network
Severity: High
Score: 8.0
STRIDE: Elevation of Privilege, Information Disclosure, Tampering
Affected Components: Web Server, Internal Firewall, Internal Network
Why: Traffic can flow from DMZ (Web Server node 5) through Internal Firewall (node 7) to Core Switch/Internal Network (node 9/8), risking lateral movement if DMZ is compromised.
References: ASVS V9.1, ASVS V10.2, CWE-284
Recommended Actions:
Strictly limit and monitor DMZ-to-internal traffic, enforce least privilege firewall rules, and segment networks.
T004: Sensitive Data Exposure on File and Directory Servers
Severity: High
Score: 8.0
STRIDE: Information Disclosure
Affected Components: File Server, Directory Server (AD)
Why: File Server (node 11) and Directory Server (node 12) store sensitive data and credentials, risking exposure if access controls are weak.
References: ASVS V9.4, ASVS V10.4, CWE-200
Recommended Actions:
Encrypt data at rest, enforce strict access controls, and audit access to sensitive servers.
T005: VPN Gateway Exposed to Credential Attacks
Severity: High
Score: 8.0
STRIDE: Spoofing, Elevation of Privilege, Denial of Service
Affected Components: Internet, VPN Gateway
Why: VPN Gateway (node 6) is reachable from the Internet (node 2) and processes credentials, making it a target for brute-force and credential stuffing.
References: ASVS V2.1, ASVS V2.2, CWE-307
Recommended Actions:
Enforce strong authentication (MFA), rate-limit login attempts, and monitor for suspicious access patterns.
| ID | Threat | Severity | Score |
|---|---|---|---|
| T001 | External Attackers Can Reach Public-Facing Web Server | High | 9.0 |
| T002 | Insufficient Input Validation on Public Web Server | High | 8.0 |
| T003 | Potential Lateral Movement from DMZ to Internal Network | High | 8.0 |
| T004 | Sensitive Data Exposure on File and Directory Servers | High | 8.0 |
| T005 | VPN Gateway Exposed to Credential Attacks | High | 8.0 |
スマートホーム
システム構成図
対象となるスマートホームの構成図はこちらです。脅威モデリングツール OWASP Threat Dragonで作成しています。自宅の住人がモバイルアプリを通じてクラウドの制御サービスにアクセスし、そのクラウドからホームルーター経由でIPカメラやスマートロック、スマートスピーカーなどのデバイスを操作する、典型的なクラウド連携型スマートホーム環境を示しています。

スマートホームのシステム構成図
Threat Thinkerの実行
ここではWeb UIを用いて実行します。
Threat ThinkerにはRAG機能があり、MarkdownやHTMLなどのファイルをアップロードしてKnowledge Baseを構築することができ、脅威推論の際に参照させることができます。今回はスマートホームのシステムなのでOWASP IoT Top 10に基づくKnowledge Baseを構築します。

OWASP IoT Top10に基づくKnowledge Baseの構築
脅威の推論で構築したKnowledge Baseを参照させるための設定をします。

構築したKnowledge Baseを脅威推論で使用
洗い出された脅威
Markdownレポートで示される上位5件の脅威は以下の通りです。モバイルアプリとクラウド間の通信が暗号化されていない可能性、クラウドからホームデバイスに送信されるコマンドの真正性が確認できないこと、クラウド側に保存される動画・ログの保護不足などが挙げられています。また、ユーザー認証が弱い場合は、第三者がデバイスを不正操作できる危険性も指摘されています。OWASP IoT Top10を参照した脅威分析の結果としても納得感のある内容となっています。
Markdownレポート
Threat Analysis Report
Threat Summary
| ID | Threat | Severity | Score |
|---|---|---|---|
| T001 | Insecure Communication Between Mobile App and Cloud Control Service | High | 9.0 |
| T002 | Lack of Authentication for Device Commands from Cloud to Home Network | High | 9.0 |
| T003 | Insecure Storage of Sensitive Video/Logs in Cloud | High | 8.0 |
| T004 | Unencrypted Video/Telemetry Data in Transit | High | 8.0 |
| T005 | Weak or Missing Authentication for Mobile App User Actions | High | 8.0 |
Threat Details
T001: Insecure Communication Between Mobile App and Cloud Control Service
Severity: High
Score: 9.0
STRIDE: Tampering, Information Disclosure, Spoofing
Affected Components: Mobile App, Cloud Control Service
Why: If communication is not encrypted and authenticated, attackers can intercept or modify control commands or device status (assumed due to unspecified protocol).
References: ASVS V9.1, ASVS V2.1, CWE-319
Recommended Actions:
Enforce TLS 1.2+ with certificate validation and mutual authentication between Mobile App and Cloud Control Service.
T002: Lack of Authentication for Device Commands from Cloud to Home Network
Severity: High
Score: 9.0
STRIDE: Spoofing, Elevation of Privilege, Tampering
Affected Components: Cloud Control Service, Home Router, IP Camera, Smart Lock, Smart Speaker
Why: If device commands are not authenticated, attackers could impersonate the cloud and control home devices (protocol and auth not specified).
References: ASVS V2.1, ASVS V4.2, CWE-287
Recommended Actions:
Require strong authentication (e.g., signed tokens or mutual TLS) for all device command channels from cloud to home devices.
T003: Insecure Storage of Sensitive Video/Logs in Cloud
Severity: High
Score: 8.0
STRIDE: Information Disclosure, Repudiation
Affected Components: Cloud Storage
Video/Logs
Why: If cloud storage is not encrypted or access-controlled, attackers or insiders could access or tamper with sensitive video/logs.
References: ASVS V10.1, ASVS V1.4, CWE-200
Recommended Actions:
Encrypt data at rest in cloud storage and enforce strict access controls and audit logging.
T004: Unencrypted Video/Telemetry Data in Transit
Severity: High
Score: 8.0
STRIDE: Information Disclosure, Tampering
Affected Components: IP Camera, Home Router, Cloud Control Service, Cloud Storage
Video/Logs
Why: Sensitive video/log data may be exposed if not encrypted during transmission (protocols not specified).
References: ASVS V9.1, ASVS V10.1, CWE-319
Recommended Actions:
Encrypt all telemetry/video data in transit using TLS 1.2+ and validate certificates at each hop.
T005: Weak or Missing Authentication for Mobile App User Actions
Severity: High
Score: 8.0
STRIDE: Spoofing, Elevation of Privilege
Affected Components: Resident User, Mobile App
Why: If the Mobile App does not enforce strong authentication, attackers could impersonate users and control devices (auth not specified).
References: ASVS V2.1, ASVS V4.2, CWE-287
Recommended Actions:
Implement strong user authentication (e.g., OAuth 2.0, MFA) in the Mobile App for all sensitive actions.
| ID | Threat | Severity | Score |
|---|---|---|---|
| T001 | Insecure Communication Between Mobile App and Cloud Control Service | High | 9.0 |
| T002 | Lack of Authentication for Device Commands from Cloud to Home Network | High | 9.0 |
| T003 | Insecure Storage of Sensitive Video/Logs in Cloud | High | 8.0 |
| T004 | Unencrypted Video/Telemetry Data in Transit | High | 8.0 |
| T005 | Weak or Missing Authentication for Mobile App User Actions | High | 8.0 |
また、Threat ThinkerではThreat Dragonのフォーマットで入力した際には推論した脅威を加えた上でThreat Dragon形式で出力をすることができます。

各要素に紐づく脅威が追加されている
おわりに
本記事では、Threat Thinkerを使ってAWSベースシステム、企業ネットワーク、スマートホームという異なる3種類のシステムに対してThreat Thinkerを用いた脅威モデリングを試しました。システム構成図を読み込ませるだけで基本的な脅威は洗い出せることが分かりました。
一方で、ビジネスロジック固有のリスクや各組織特有の運用に依存する問題などは人間が見ていく必要があります。LLMによる脅威モデリングは専門家のレビューの代替ではなく、初期のたたき台を用意したり、抜け漏れ防止のための補助として活用するのが現実的だと考えています。OWASP IoT Top 10 のようなガイドラインをKnowledge Baseとして取り込めば、ドメイン特化の推論も可能になります。気になった方は、お手元のシステム構成図を用いてThreat Thinkerによる脅威モデリングを試していただけると幸いです。
Discussion