Skip to content

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.

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 Tree
3. What are you going to do? → Controls, mitigations, risk acceptance
4. Did you do a decent job? → Review, iteration, validation

Step 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)
  1. STRIDE — Microsoft, 1999, mnemonic for six threat classes mapped to CIA + authenticity + accountability + availability:

    LetterThreatProperty ViolatedReal Example
    SSpoofingAuthenticityPhishing login (no MFA → identity spoofing)
    TTamperingIntegrityJWT signature not verified (alg=none attack)
    RRepudiationAccountabilityNo audit log on data export
    IInformation DisclosureConfidentialityStack trace returned on 500 with SQL query
    DDenial of ServiceAvailabilityNo rate limit on /login (credential stuffing)
    EElevation of PrivilegeAuthorizationIDOR 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.

  2. DREAD — Microsoft, 1999, a risk-scoring rubric (1–10 each):

    LetterQuestion
    DDamage: how bad is the worst case?
    RReproducibility: how easily can it be reproduced?
    EExploitability: how much effort to exploit?
    AAffected users: how many?
    DDiscoverability: 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.

  3. PASTA (Process for Attack Simulation and Threat Analysis) — 7 stages:

    1. Define objectives (business, compliance).
    2. Define technical scope (architecture, technologies).
    3. Application decomposition (DFD, data classification).
    4. Threat analysis (attack surface, threat agents).
    5. Vulnerability analysis (weaknesses, known CVEs).
    6. Attack modeling (attack trees, kill chains).
    7. Risk assessment and countermeasure selection.
  4. 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?
  5. 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)
  6. 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).
MethodOutputBest For
STRIDE per DFD elementList of threats by propertyDesign review of a single feature
DREAD scoringRisk score per threatTriage of an existing list
PASTA7-stage analysis documentCompliance-driven enterprise work
Attack TreesTree with cost/probability annotationsAdversary simulation, red-team planning
LINDDUNPrivacy threat listGDPR/CCPA compliance work
TrikeThreat list + risk acceptanceOpen-source project threat modeling
VASTVisual board with one threat per cardAgile sprint integration
Terminal window
# Install OWASP Threat Dragon (open-source threat modeling tool)
# Threat Dragon is a Node.js web app that produces DFDs + STRIDE
git clone https://github.com/OWASP/threat-dragon
cd 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'
@startuml
skinparam linetype ortho
square "User" as user
square "Web App" as web
database "DB" as db
user -down-> web : HTTPS / login
web -down-> db : SQL (parameterized)
@enduml
plantuml dfd.puml # produces dfd.png
# STRIDE matrix for an element — write a small Python script
python3 -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 mitigations
curl -s https://ssl-config.mozilla.org/guidelines/latest.json | \
jq '.configurations.intermediate'
# Run an automated threat model against a Kubernetes manifest
pip install kube-hunter
kube-hunter --remote <cluster-ip> --active # shows known attack paths
# For a cloud account, enumerate trust boundaries via IAM
aws iam list-roles | jq '.Roles[].RoleName'
# Each role with a trust policy allowing another account is a cross-trust-boundary flow.

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 flow rules), Cloudflare WAF rate-limit rules.
  • E (Elevation of Privilege) — privileged-group membership changes (Windows Security 4720/4732, AWS CloudTrail AddUserToGroup), anomalous role assumption (CloudTrail AssumeRole with 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).

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-patternWhy it failsHow to fix
DFD without trust boundariesThreat surface invisibleMark every boundary in dashed red
STRIDE without DREADNo prioritizationScore every STRIDE item
DREAD without STRIDENo threats to scoreUse STRIDE per DFD element first
Model only the happy pathMisses attack variantsInclude error, retry, and admin paths
Skip trust boundaries in CI/CDPipeline compromise = code compromiseInclude build server in the DFD
Treat TM as a one-time activitySystem evolves; threats evolveRe-run on every major change

⚠️ What caught me: A threat model for an OAuth2 callback that covered everything except the redirect_uri parameter. We modeled the code exchange, the consent screen, the token revocation — but assumed redirect_uri was 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 with redirect_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.

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:

  1. 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.
  2. Run STRIDE per DFD element (4 elements × 6 threats = 24 candidates). Reduce to the realistic subset (typically 8–12).
  3. Score each realistic threat with DREAD; produce a prioritized list.
  4. Pick the top 3 threats; for each, propose a control and the telemetry that control produces.
  5. 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).

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.

→ F10 (Security Models & Principles) · F12 (Cyber Kill Chain & MITRE ATT&CK) · S10 (Secure Software Development Lifecycle)