Threat modeling
Threat modeling is the practice of systematically asking "what can go wrong with this system, and what are we doing about it" before the system exists, and re-asking it as the system changes. This page covers the STRIDE framework for enumerating threat categories, data flow diagrams and trust boundaries as the structured input a STRIDE pass actually needs, attack trees as a complementary technique for reasoning about a single high-value target, and why a threat model that isn't revisited is closer to a historical document than a security control.
Before you build a treehouse, you walk around the tree and ask: could someone climb up the ladder when I'm not looking? Could the rope holding the platform snap? Could a rival kid sneak in through the back while I'm watching the front door? You ask these questions with the blueprint in hand, before a single board is nailed down — not after the treehouse falls and you're figuring out which board gave way. Threat modeling is that walk-around, done on paper, for software.
What threat modeling is, and why timing is the whole point
A threat model is not a document you write once and file away — it's the output of a repeatable exercise: look at a system's design, enumerate the ways an adversary could abuse it, and decide, for each one, whether you're mitigating it, accepting the risk, transferring it, or explicitly deferring it. The output is typically a list of threats mapped to the part of the system they affect, a severity or likelihood rating, and a disposition. What makes it threat modeling rather than a generic bug hunt is that it starts from the system's structure — how data moves, who's on each side of a boundary — rather than from a list of known vulnerability classes to grep for.
The reason this belongs at design time, not after deployment, is cost. The oft-cited (and consistently reproduced, in various forms, by IBM's Systems Sciences Institute research and subsequent industry studies) finding is that a defect caught in design costs roughly an order of magnitude less to fix than the same defect caught in testing, and one to two further orders of magnitude less than the same defect caught in production. A missing authorization check found on a whiteboard is a five-minute edit to a data flow diagram. The same missing check found by a penetration tester is a scramble to patch, retest, and explain. Found by an attacker in production, it's an incident response engagement, a possible breach notification, and a permanent scar on customer trust. Threat modeling is how a team spends five minutes instead of five weeks on the same underlying gap.
STRIDE: six categories for one question, "what can go wrong here"
STRIDE, developed at Microsoft in the late 1990s (Loren Kohnfelder and Praerit Garg), is a mnemonic for six categories of threat, each tied to a security property being violated. Applied to every element in a data flow diagram, it turns "think about security" — an instruction too vague to act on — into six concrete questions per element.
- Spoofing — can an attacker convincingly pretend to be someone or something they're not? Violates authentication. Example: a service accepting a client-supplied user ID header with no signature verifying it came from a trusted identity provider.
- Tampering — can an attacker modify data or code without authorization? Violates integrity. Example: a build artifact fetched over plain HTTP with no checksum, allowing substitution in transit.
- Repudiation — can an actor deny having performed an action, with nothing to prove otherwise? Violates non-repudiation. Example: an admin API with no audit log, so a destructive action can't be traced to an account afterward.
- Information disclosure — can data reach someone who shouldn't see it? Violates confidentiality. Example: a verbose error response that echoes a stack trace with internal hostnames back to an unauthenticated caller.
- Denial of service — can an attacker degrade or block legitimate access? Violates availability. Example: an unauthenticated endpoint that triggers an expensive database query, with no rate limit in front of it.
- Elevation of privilege — can an actor gain capabilities they weren't granted? Violates authorization. Example: a background job that runs with admin credentials to do one narrow task, so any bug in that job is a path to full admin access.
STRIDE is deliberately a checklist, not a scoring formula — it doesn't tell you how bad a given spoofing threat is, only that you haven't finished until you've asked the spoofing question about every trust-boundary crossing. Severity and prioritization come afterward, typically from a separate model like DREAD or CVSS, or from plain team judgment about blast radius and exploitability.
STRIDE's value is coverage, not precision. Six categories asked consistently against every element of a real design catch far more than an unstructured "does anything look sketchy" review, because the mnemonic forces you past the two or three threat types that happen to be top of mind that week.
Data flow diagrams and trust boundaries: the input STRIDE actually needs
STRIDE is a question set, but you can't apply it to a system you haven't first drawn. The standard input is a data flow diagram (DFD): a small set of shapes — external entities, processes, data stores, and the data flows connecting them — that map how information actually moves through the system, not how the org chart or the codebase's package structure is organized. The distinction matters: a DFD drawn from the architecture diagram in a design doc is usually close, but the diagram that matters for threat modeling is the one that reflects where data really crosses between components, including the paths engineers tend to leave out of the "official" diagram, like a debug endpoint or a cron job that reads production data directly.
Layered onto the DFD are trust boundaries — lines marking every point where data crosses from one level of trust to another: from an anonymous user's browser into your application, from your application into a database it authenticates to, from your VPC into a third-party API. Trust boundaries are where STRIDE analysis concentrates, because a threat inside a single trust zone (your own service talking to its own cache, both of which you control end to end) is a much smaller concern than a threat at a boundary where an external, less-trusted actor is on one side. The diagram below shows the minimum viable version of this: a user crosses a trust boundary to reach a web app, which then talks to a database inside the trusted zone.
Real systems have more than one boundary — a service mesh sidecar, a third-party payment API, an internal admin panel reachable only from a VPN, each is its own line on the diagram, and each gets its own STRIDE pass. A system with no trust boundaries drawn at all is a sign the DFD hasn't been done properly yet, not a sign the system has no external risk. This is also where the secure SDLC connects to this page: the DFD-and-STRIDE exercise is meant to run during the design phase of that lifecycle, before the first line of implementation code, and its output — the list of mitigations decided on — becomes concrete requirements for the phases that follow, like which inputs need validation and which calls need authentication.
Attack trees: going deep on one target instead of wide across the system
STRIDE and a DFD are a breadth-first tool — they push you to look at every element and every boundary once. Attack trees, a technique formalized by Bruce Schneier in the late 1990s, are the complementary depth-first tool: you start from a single attacker goal at the root ("exfiltrate the customer database," "impersonate an admin user") and decompose it into the specific sub-goals or steps that would achieve it, branching further until each leaf is a concrete, evaluable action. Branches can be marked AND (all sub-steps required) or OR (any one suffices), which makes the tree useful for estimating the cheapest viable path to the goal, not just the full set of paths.
The two techniques are usually run together rather than as alternatives: a STRIDE-over-DFD pass surfaces the broad list of "these threats exist," and an attack tree gets built for the handful that are high-severity or hard to reason about in one line — typically the ones a security champion (see security culture and champions) or a red team wants to think through step by step, such as "how would someone actually get from a compromised low-privilege service account to production database access." Attack trees are less suited to full-system coverage because building one per element doesn't scale, but they're the right tool once you already know which target deserves the deeper look.
A continuous practice, not a one-time deliverable
The most common way threat modeling fails in practice isn't skipping it — most teams that adopt it do at least one pass, often as part of a design review. It's treating that one pass as permanent. A threat model reflects the system's architecture and trust boundaries at a specific point in time; every subsequent change to that architecture — a new integration added, a service split in two, a trust boundary that used to sit in front of an internal admin tool quietly removed because "it's only reachable from the VPN now" — can invalidate parts of the original model without anyone noticing, because nothing forces the model to be re-checked.
Threat modeling that works long-term is scoped and triggered like any other recurring engineering activity: revisit the model whenever a new trust boundary is introduced (a new external integration, a new microservice, a new deployment target), whenever a major feature changes what data flows where, and on a fixed cadence regardless (quarterly is a common baseline for actively developed systems) as a backstop against changes nobody flagged. Some teams keep the DFD as a living diagram next to the architecture docs and require the STRIDE pass to be re-run as a checklist item on any PR that touches it; others run a lighter recurring workshop. Either works, as long as "we did threat modeling" stops meaning a single meeting from eighteen months ago. See what is DevSecOps for how this recurring, shift-left posture fits the broader practice, and the tooling landscape for where dedicated threat modeling tools (Microsoft Threat Modeling Tool, OWASP Threat Dragon) fit alongside everything else in the pipeline.
A threat model that lists threats but records no disposition for each one isn't finished — "spoofing possible on the login endpoint" is an observation, not a decision. Every identified threat needs an explicit call: mitigated (and how), accepted (and by whom, with a documented reason), transferred, or deferred with a tracked follow-up. A threat model full of unresolved observations gives a false sense of coverage while leaving every actual decision unmade.
1. Why is it far cheaper to catch a design flaw during threat modeling than to catch the same flaw in production? 2. Name all six STRIDE categories and the security property each one violates. 3. What is a trust boundary, and why does STRIDE analysis concentrate on the points where one is crossed? 4. How do attack trees complement a STRIDE-over-DFD pass, and why does threat modeling need to be a recurring practice rather than a one-time document?
Check your answers
- Fixes get more expensive the later they're found — a design-time fix is a small edit to a diagram or plan, while the same flaw found in testing costs meaningfully more to patch and retest, and found in production it can mean an incident response engagement, a breach notification, and lasting damage to customer trust — roughly an order of magnitude of cost added at each later stage.
- Spoofing (authentication), Tampering (integrity), Repudiation (non-repudiation), Information disclosure (confidentiality), Denial of service (availability), Elevation of privilege (authorization).
- A trust boundary is a point where data crosses from one level of trust to another, such as from an anonymous user into your application or from your network into a third-party API. STRIDE analysis concentrates there because threats confined entirely within a single trust zone you control end to end are a far smaller concern than threats at the points where a less-trusted actor is on one side.
- STRIDE-over-DFD is breadth-first, surfacing the full list of plausible threats across every element; attack trees are depth-first, decomposing one specific high-severity attacker goal into the concrete steps that would achieve it, useful for reasoning about the cheapest viable path to that goal. Threat modeling needs to be recurring because architecture keeps changing after the first pass — new integrations, new services, removed boundaries — and nothing else forces the model to be re-checked against the system as it actually is now.