Zero to Hero: Cybersecurity Architecture Masterclass, Module 2
← Module 1: Core Mental Models · Module 2: Proactive Design · Module 3: Cloud-Native Hardening →
11 min read
TL;DR
- STRIDE threat modeling is a checklist for finding design-level vulnerabilities before code exists: Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege
- Run it against a data-flow diagram, not against code — every process, data store, and trust boundary gets checked against all six categories
- DREAD risk scoring turns “this is a threat” into a number, so you can prioritize which findings become engineering tickets first
- Trust boundaries — anywhere data crosses from one privilege level to another — are where most real threats concentrate
- Free, code-based tools (
pytm, OWASP Threat Dragon) let you version-control your threat model the same way you version-control infrastructure - STRIDE run once at design time catches classes of bugs that a penetration test only catches after the system already shipped
The Big Picture: STRIDE Threat Modeling in One Checklist
Every element in a system — a process, a data store, a data flow, an external entity — can fail in up to six ways. STRIDE threat modeling names them so you check for all six instead of whichever one happened to occur to you.
STRIDE THREAT MODEL — APPLIED PER SYSTEM ELEMENT
──────────────────────────────────────────────────────────────
Threat Category Security Property Violated
──────────────────────────────────────────────────────────────
S Spoofing Authenticity — are you who you say?
T Tampering Integrity — was this modified?
R Repudiation Non-Repudiation— can this be denied?
I Information Disclosure Confidentiality— who else can read this?
D Denial of Service Availability — can this be starved?
E Elevation of Privilege Authorization — can this reach more than it should?
──────────────────────────────────────────────────────────────
↑ maps directly onto the Extended CIA Triad from Module 1
STRIDE threat modeling is a systematic way to find design flaws before a single line of code exists, by checking every element of a system against these six failure modes instead of relying on whoever’s reviewing the design to think of them unprompted.
Why “Shift Left” Needs a Checklist, Not Good Intentions
Module 1 closed by naming the “Shift Left Myth” — teams that call a CI security scanner “shifting left” when the actual architecture was never reviewed at the design phase at all. A CI scan finds vulnerabilities in code that already exists. STRIDE finds the ones that don’t need code to exist yet, because they’re baked into the design: a service that trusts an internal network by IP address, a queue with no message-origin verification, an admin API reachable from the same trust zone as public traffic.
A team building a new internal billing service skips a design review — “it’s internal, it’s fine” — and ships it trusting any caller on the VPC. Eight months later, a compromised marketing-analytics pod (unrelated team, unrelated purpose, same VPC) calls the billing API directly and issues refunds. Nothing was “hacked” in the traditional sense. The design simply never asked: what happens if something on this network isn’t who we assumed?
That’s a Spoofing failure, and STRIDE would have surfaced it in an hour-long design review, months before the analytics pod existed.
Running STRIDE Against a Data-Flow Diagram
STRIDE is applied to a Data-Flow Diagram (DFD) — not to source code, and not to infrastructure diagrams showing subnets and security groups. A DFD has four element types, and each type is only vulnerable to a subset of STRIDE:
Element Type Vulnerable To
────────────────── ─────────────────────────────────
External Entity Spoofing, Repudiation
Process Spoofing, Tampering, Repudiation,
Info Disclosure, DoS, Elevation
Data Store Tampering, Info Disclosure, DoS,
(Repudiation if no access logging)
Data Flow Tampering, Info Disclosure, DoS
Processes are checked against all six categories because they’re where identity, logic, and privilege all live. Data stores can’t “spoof” anything — but they can absolutely be read or written by someone who shouldn’t, or overwhelmed.
Trust boundaries are drawn as dashed lines across the diagram anywhere a data flow crosses from one privilege or trust level to another: public internet → load balancer, application tier → database tier, one team’s service → another team’s service, on-prem → cloud. Every element sitting directly on a trust boundary gets checked first, because that’s structurally where real threats concentrate — an internal-only process that never sees a trust boundary is a much lower priority than an internet-facing one processing untrusted input.
The billing-service incident above is a trust-boundary failure by definition: the design never drew a boundary between “our service” and “anything else on the VPC,” so nothing on that (missing) boundary was ever checked.
Working the Six Categories
Spoofing — Can an entity convincingly pretend to be something it isn’t? Mitigations: mutual TLS, signed service tokens, SPIFFE/SPIRE identities instead of IP-based trust (Module 1’s Zero Trust principle, applied concretely).
Tampering — Can data be modified in transit or at rest without detection? Mitigations: TLS in transit, checksums/signatures on artifacts, database-level integrity constraints, immutable audit logs.
Repudiation — Can an actor perform an action and later credibly deny it? Mitigations: signed, centrally-shipped audit logs (CloudTrail, Kubernetes audit logs) that the actor cannot modify after the fact — this is why Module 1 called non-repudiation an architectural requirement, not a compliance checkbox.
Information Disclosure — Can data reach an entity that shouldn’t see it? Mitigations: encryption at rest and in transit, least-privilege IAM, field-level access control for sensitive data classes.
Denial of Service — Can an entity be starved of resources it needs to function? Mitigations: rate limiting, autoscaling with sane ceilings, circuit breakers, resource quotas per tenant.
Elevation of Privilege — Can an entity reach capabilities beyond what it was granted? Mitigations: strict RBAC, no ambient authority, explicit privilege boundaries between services — this is the category both the iam:PassRole privilege-escalation pattern (covered in the IAM series) and misconfigured S3 buckets escalating to admin access belong to.
Scoring What You Find: DREAD
STRIDE tells you what kind of threat exists. It says nothing about how bad it is. A dozen findings with no prioritization is not actionable — DREAD converts each finding into a 0–10 score across five dimensions so engineering can triage like any other backlog:
D Damage Potential — how bad is the worst case if exploited?
R Reproducibility — how reliably can it be triggered?
E Exploitability — how much skill/access does it require?
A Affected Users — how much of the system/user base is exposed?
D Discoverability — how easy is it to find unassisted?
DREAD score = average of the five (0–10 scale)
The billing-service Spoofing finding above scores high on Damage (financial loss), high on Reproducibility (any pod on the VPC, repeatably), moderate on Exploitability (requires being on the VPC — not zero-effort, but not hard either), high on Affected Users (the entire billing system), and low-to-moderate on Discoverability (not obvious without VPC access, but not hidden either). That combination — high damage, high reproducibility — is exactly the profile that goes to the top of the backlog, above findings that are theoretically worse but require nation-state-level access to trigger.
Doing This as Code, Not a Whiteboard Session
A whiteboard threat model is useful for a workshop and useless six months later when the architecture has changed and nobody updates the photo. pytm and OWASP Threat Dragon let you define the data-flow diagram and its trust boundaries as a file, review it in a pull request, and regenerate the DFD and a STRIDE finding report on every change.
# threatmodel.py (pytm)
from pytm import TM, Server, Datastore, Dataflow, Boundary
tm = TM("Billing Service")
internet = Boundary("Public Internet")
internal = Boundary("Internal VPC")
api = Server("Billing API")
api.inBoundary = internal
db = Datastore("Billing DB")
db.inBoundary = internal
caller = Dataflow(api, db, "Query balance")
caller.protocol = "PostgreSQL"
caller.isEncrypted = True
tm.process()
# Generate the DFD and run the STRIDE analysis
$ python3 threatmodel.py --dfd | dot -Tpng -o dfd.png
$ python3 threatmodel.py --report json > findings.json
# Findings surface automatically per element/boundary, e.g.:
# [ELEVATION OF PRIVILEGE] Billing API -> Billing DB crosses no
# authentication boundary check; caller identity is not verified
# before query execution.
The model lives next to the code it describes, diffs like any other file, and a reviewer sees exactly what trust boundary changed when a new dependency gets added — instead of discovering it in production eight months later.
Production Gotchas
A threat model with no owner goes stale in one sprint. Assign the DFD file the same ownership as the service’s Terraform or Helm chart — whoever changes the architecture updates the model in the same PR.
STRIDE without trust boundaries drawn is just a vocabulary exercise. Teams sometimes run through all six letters against a whole system at once with no boundaries marked, producing a vague list nobody acts on. Draw the boundaries first; findings should cluster around them.
DREAD scores drift toward “everything is a 7” without calibration. Anchor each dimension with 2–3 concrete example findings from your own systems before scoring new ones, or every finding regresses to the mean and the prioritization signal disappears.
A code-based threat model is not a substitute for a design review conversation. pytm output is a starting point for discussion between the architect and the team, not a report to file away unread.
Framework Alignment
| Framework | Control / ID | Architectural Mapping |
|---|---|---|
| NIST CSF 2.0 | ID.RA-01 | Asset vulnerabilities are identified and documented — threat modeling is the design-phase mechanism for this. |
| NIST SP 800-207 | Zero Trust | Trust boundary analysis is the direct architectural expression of “never trust, always verify.” |
| ISO 27001:2022 | 8.25 | Secure development life cycle — threat modeling required at the design phase, not just pre-release testing. |
| SOC 2 | CC7.1 | The organization identifies and evaluates changes that could impact the system of internal control. |
Key Takeaways
- STRIDE checks every system element against six named failure modes so nothing gets skipped because no one thought of it
- Run it against a data-flow diagram with trust boundaries explicitly drawn — findings cluster where boundaries are
- DREAD turns qualitative findings into a prioritized, comparable backlog
- Code-based threat modeling (
pytm, Threat Dragon) keeps the model current instead of a stale whiteboard photo - A threat model needs an owner tied to the architecture it describes, or it goes stale in one sprint
What’s Next
Module 2 gave you the process for finding design flaws before code exists. Module 3 takes one specific, high-stakes trust boundary — the AWS identity perimeter — and shows exactly how IMDSv2, IAM policy design, and infrastructure-as-code scanning close the Elevation of Privilege and Spoofing findings that STRIDE surfaces most often in cloud-native systems.
Next: Module 3: Cloud-Native Hardening — Securing the AWS Identity Perimeter
Get the full masterclass in your inbox → linuxcent.com/subscribe