Threat Modeling
Threat modeling is the structured practice of asking “what can go wrong” before code ships — and the structured practice of asking “which design assumption did the attacker violate” after a breach. STRIDE, DREAD, PASTA, attack trees, and Adam Shostack’s four-question framework are not interchangeable methodologies; they answer different questions. Choosing the right one for a feature is the difference between a useful design review and a checkbox exercise. This module walks through each model with concrete examples (an OAuth2 callback, an internal admin API, a cloud storage bucket) so you can run a real threat-model session, not just read about one.
The Core Concept
Section titled “The Core Concept”Every threat model has the same four questions (Shostack, “Threat Modeling: Designing for Security”, 2014):
1. What are you building? → Data Flow Diagram (DFD)2. What can go wrong? → STRIDE / PASTA / Attack Tree3. What are you going to do? → Controls, mitigations, risk acceptance4. Did you do a decent job? → Review, iteration, validationStep 1 produces a Data Flow Diagram (DFD). External entities (squares), processes (circles), data stores (parallel lines or cylinders), and data flows (arrows). Trust boundaries (dashed lines) separate zones with different trust levels — every cross-boundary flow is a STRIDE candidate.
┌────────────┐ ┌────────────┐ ┌────────────┐│ Browser │ ──→ │ Web App │ ──→ │ Database ││ (external) │ TLS │ (process) │ SQL │ (data store)│└────────────┘ └────────────┘ └────────────┘ │ │ OAuth code exchange ▼ ┌────────────┐ │ IdP │ │ (external) │ └────────────┘ ▲ trust boundary (Internet → DMZ) ▲ trust boundary (DMZ → internal IdP) ▲ trust boundary (DMZ → internal DB)How It Works
Section titled “How It Works”-
STRIDE — Microsoft, 1999, mnemonic for six threat classes mapped to CIA + authenticity + accountability + availability:
Letter Threat Property Violated Real Example S Spoofing Authenticity Phishing login (no MFA → identity spoofing) T Tampering Integrity JWT signature not verified (alg=none attack) R Repudiation Accountability No audit log on data export I Information Disclosure Confidentiality Stack trace returned on 500 with SQL query D Denial of Service Availability No rate limit on /login(credential stuffing)E Elevation of Privilege Authorization IDOR on /api/users/<id>(test sequential IDs)Each cell maps a STRIDE threat to one of the four CIA+ properties. The “E” (EoP) is the one most often missed because it requires modeling the authorization rules, not just the data flow.
-
DREAD — Microsoft, 1999, a risk-scoring rubric (1–10 each):
Letter Question D Damage: how bad is the worst case? R Reproducibility: how easily can it be reproduced? E Exploitability: how much effort to exploit? A Affected users: how many? D Discoverability: how easily is it found? Sum / 5 = risk score. DREAD is widely criticized for being subjective (“Discoverability” is attacker-controlled, not a system property) but useful for triage when paired with STRIDE.
-
PASTA (Process for Attack Simulation and Threat Analysis) — 7 stages:
- Define objectives (business, compliance).
- Define technical scope (architecture, technologies).
- Application decomposition (DFD, data classification).
- Threat analysis (attack surface, threat agents).
- Vulnerability analysis (weaknesses, known CVEs).
- Attack modeling (attack trees, kill chains).
- Risk assessment and countermeasure selection.
-
LINDDUN — privacy-focused threat modeling. The mnemonic:
- Linkability — can data be linked across contexts?
- Identifiability — can a subject be identified from data?
- Non-repudiation — can a subject deny an action?
- Detectability — can a subject’s presence be detected?
- Disclosure of information — does data reveal more than necessary?
- Unawareness — is the user aware of data collection?
- Non-compliance — does the system violate privacy regulations?
-
Attack Trees — formalize a goal as a tree of sub-goals. The root is “attacker objective” (e.g., “Exfiltrate customer DB”). Children are alternatives at each level; AND-nodes (all required) and OR-nodes (any sufficient). Each leaf can be annotated with cost, probability, defender cost.
Goal: Exfiltrate customer DB├── OR ─ Compromise admin credentials│ ├── Phish the admin (cost: low, success: medium)│ ├── SQLi on admin login (cost: medium, success: low)│ └── Buy access on forum (cost: low, success: low)└── OR ─ Direct DB access├── Expose DB to internet (misconfig) (cost: low, success: low)└── SSRF → internal DB (cost: high, success: medium) -
Trust boundaries are the most important modeling artifact. Every data flow that crosses a trust boundary is a STRIDE candidate. Typical boundaries:
- Internet → DMZ.
- DMZ → internal network.
- User → web app (logged-in vs anonymous).
- Web app → database.
- Web app → IdP (OAuth/OIDC).
- Microservice A → Microservice B (different privilege sets).
- Container → host kernel.
- Tenant A → Tenant B (multi-tenant).
Common Techniques
Section titled “Common Techniques”| Method | Output | Best For |
|---|---|---|
| STRIDE per DFD element | List of threats by property | Design review of a single feature |
| DREAD scoring | Risk score per threat | Triage of an existing list |
| PASTA | 7-stage analysis document | Compliance-driven enterprise work |
| Attack Trees | Tree with cost/probability annotations | Adversary simulation, red-team planning |
| LINDDUN | Privacy threat list | GDPR/CCPA compliance work |
| Trike | Threat list + risk acceptance | Open-source project threat modeling |
| VAST | Visual board with one threat per card | Agile sprint integration |
Hands-On Commands
Section titled “Hands-On Commands”# Install OWASP Threat Dragon (open-source threat modeling tool)# Threat Dragon is a Node.js web app that produces DFDs + STRIDEgit clone https://github.com/OWASP/threat-dragoncd threat-dragon && npm install && npm start # serves on http://localhost:8080
# Install IriusRisk Community Edition (Docker image)docker run -d --name iriusrisk \ -p 8080:8080 \ -e IRIUS_DB_HOST=localhost -e IRIUS_DB_PORT=5432 \ iriusrisk/iriusrisk-community-edition:latest
# Generate a STRIDE report from a DFD described in YAML (tmde tool)pip install pytm # Pythonic Threat Modeling library# Usage: describe a system in pytm; render to PlantUML DFD + STRIDE report# Example: from tm import TM, Actor, Boundary; ...
# PlantUML render of a simple DFD (write to dfd.puml)cat > dfd.puml <<'EOF'@startumlskinparam linetype orthosquare "User" as usersquare "Web App" as webdatabase "DB" as dbuser -down-> web : HTTPS / loginweb -down-> db : SQL (parameterized)@endumlplantuml dfd.puml # produces dfd.png
# STRIDE matrix for an element — write a small Python scriptpython3 -c "stride = { 'Spoofing': 'authenticity', 'Tampering': 'integrity', 'Repudiation':'accountability', 'Info Disc': 'confidentiality', 'DoS': 'availability', 'EoP': 'authorization',}for k, v in stride.items(): print(f'{k:14s} violates {v}')"
# Use the Mozilla SSL Config Generator to verify a TLS configuration meets threat-model mitigationscurl -s https://ssl-config.mozilla.org/guidelines/latest.json | \ jq '.configurations.intermediate'
# Run an automated threat model against a Kubernetes manifestpip install kube-hunterkube-hunter --remote <cluster-ip> --active # shows known attack paths
# For a cloud account, enumerate trust boundaries via IAMaws iam list-roles | jq '.Roles[].RoleName'# Each role with a trust policy allowing another account is a cross-trust-boundary flow.Detection — Defender View
Section titled “Detection — Defender View”Threat models are not detection mechanisms — they are design artifacts. The link to detection is: each STRIDE threat should have a corresponding control and that control should generate telemetry.
- S (Spoofing) — MFA alerts, impossible-travel alerts (Azure AD Identity Protection, Cloudflare Access), certificate pinning failures, anomalously-low TLS versions.
- T (Tampering) — file integrity monitoring (AIDE, Tripwire, OSSEC), signed-package verification (
rpm -V), Git commit signatures, TLS 1.3 AEAD tag failures. - R (Repudiation) — append-only audit logs (CloudTrail with log file validation, AWS CloudTrail Insights), signed event logs (Linux auditd with the integrity sub-system), WORM storage.
- I (Information Disclosure) — DLP alerts (McAfee, Symantec), secret-scanning alerts (GitHub Secret Scanning, GitGuardian), bucket policy alerts (Macie).
- D (Denial of Service) — rate-limit counters, SYN-flood alerts (Suricata
flowrules), Cloudflare WAF rate-limit rules. - E (Elevation of Privilege) — privileged-group membership changes (Windows Security 4720/4732, AWS CloudTrail
AddUserToGroup), anomalous role assumption (CloudTrailAssumeRolewith new source IP).
MITRE ATT&CK T1078 — Valid Accounts (the “S” spoofing technique, mapped from STRIDE).
MITRE ATT&CK T1485 — Data Destruction (the “T” tampering technique at scale; also a DoS).
MITRE ATT&CK T1562 — Impair Defenses (a repudiation tactic — attacker clears logs to repudiate actions).
MITRE ATT&CK T1068 — Exploitation for Privilege Escalation (the “E” EoP technique, mapped from STRIDE).
Wazuh rule 920 / 501 — Audit log cleared [verify — Security Event ID 1102 on Windows, ausearch -m ADD_USER on Linux].
Wazuh rule 920 / 502 — Failed login followed by success from same source (a “S” spoofing indicator).
Evasion Techniques
Section titled “Evasion Techniques”Under-mining the model. The single most common threat-modeling failure is incomplete scope. A team models the application and forgets the CI/CD pipeline, the secrets manager, the SSH bastion, the cloud account IAM. Real breach paths usually cross these un-modeled boundaries. SolarWinds 2020: the application was well-modeled; the build server was not.
Treating the diagram as the deliverable. A DFD with no STRIDE per element is just an architecture diagram. The threat model is the analysis, not the picture.
Modeling the happy path only. DFDs that show only the OAuth2 happy path miss the threat that an attacker presents an OAuth code to multiple sessions, or that an IdP issues tokens for inactive accounts (a real bug — see Okta 2022).
Skipped trust boundaries. A DFD that does not mark the trust boundary between the DMZ and the internal IdP implicitly treats the whole network as one trust zone. This is how a pivot becomes “low effort.”
Using DREAD alone. DREAD without STRIDE produces scores without threats; you cannot mitigate a number.
| Anti-pattern | Why it fails | How to fix |
|---|---|---|
| DFD without trust boundaries | Threat surface invisible | Mark every boundary in dashed red |
| STRIDE without DREAD | No prioritization | Score every STRIDE item |
| DREAD without STRIDE | No threats to score | Use STRIDE per DFD element first |
| Model only the happy path | Misses attack variants | Include error, retry, and admin paths |
| Skip trust boundaries in CI/CD | Pipeline compromise = code compromise | Include build server in the DFD |
| Treat TM as a one-time activity | System evolves; threats evolve | Re-run on every major change |
⚠️ What caught me: A threat model for an OAuth2 callback that covered everything except the
redirect_uriparameter. We modeled the code exchange, the consent screen, the token revocation — but assumedredirect_uriwas a fixed value. In fact, the spec allows it to be attacker-controlled if the IdP’s validation is permissive. The customer shipped, and a phishing attack sent users to the real IdP withredirect_uri=https://attacker.tld/callback. The IdP happily returned the auth code to the attacker’s HTTPS endpoint. Lesson: every parameter in the protocol spec is a STRIDE candidate, not just the ones you think the user controls.
Lab Practice
Section titled “Lab Practice”VM: Ubuntu 22.04 with Docker, python3-pytm, plantuml, kube-hunter. An AWS sandbox account with one VPC, one EC2, one RDS, one IAM role.
Objective:
- Pick a feature of the lab architecture (e.g., “user logs into web app, which queries RDS”). Produce a DFD with at least 3 trust boundaries in PlantUML. Render to PNG.
- Run STRIDE per DFD element (4 elements × 6 threats = 24 candidates). Reduce to the realistic subset (typically 8–12).
- Score each realistic threat with DREAD; produce a prioritized list.
- Pick the top 3 threats; for each, propose a control and the telemetry that control produces.
- Build an attack tree for “exfiltrate the RDS contents”; include at least 5 leaves with cost/success annotations.
Expected flag format: flag{dfd_rendered_stride_12_realistic_dread_top3_attacktree_5leaves}.
Detection that should fire: the controls in the top-3 must produce concrete telemetry — e.g., AWS CloudTrail log of a ModifyDBInstance call (a tampering indicator); CloudWatch alarm on RDS CPU (a DoS indicator); GuardDuty alert on anomalous API access (an EoP indicator).
Interview Questions
Section titled “Interview Questions”Conceptual: name the six STRIDE categories and the security property each violates. Why is “Elevation of Privilege” sometimes considered a composite threat?
Technical: walk through a STRIDE analysis for an HTTP API endpoint that accepts a JWT bearer token, looks up the user in Postgres, and returns the user’s order history. Identify at least one threat per STRIDE letter.
Scenario: a customer says “we do threat modeling — we have OWASP Top 10 mapped to our app.” Explain why the OWASP Top 10 is not a threat model and what they are missing.
What is the difference between an attack tree and a STRIDE analysis? When would you use one over the other?
Technical: draw the DFD and identify the trust boundaries for an OAuth2 authorization-code flow between a third-party app, your IdP, and your resource server. List three STRIDE threats that exist specifically because the OAuth2 spec allows the redirect_uri to be a parameter.
Related Modules
Section titled “Related Modules”→ F10 (Security Models & Principles) · F12 (Cyber Kill Chain & MITRE ATT&CK) · S10 (Secure Software Development Lifecycle)