What threat modeling is: the four questions
Threat modeling is four questions asked at design time, before code exists — what are we building, what can go wrong, what do we do about it, did we do a good enough job. It catches the flaws a pentest finds too late to cheaply fix.
The whiteboard says “users can share a document by emailing a link.” Everyone nods; the ticket gets pointed; the sprint starts. Six months later a researcher mails your security inbox: the share link is a signed URL whose signature covers the document id but not the permission — flip one query parameter from ?role=viewer to ?role=editor and any link-holder can edit. There is no bug to patch. The check that should grant edit rights was never designed to exist; the URL was trusted to carry truth it could not enforce. Fixing it means re-architecting how sharing works, in production, under disclosure pressure. That whiteboard moment was the cheapest point this flaw would ever cost — and nobody asked the one question that would have caught it: what can go wrong here?
By the end of this lesson you’ll be able to state the four questions of threat modeling, place the activity correctly in the SDLC, and explain why a design-time flaw is the most expensive kind of bug to find late.
What threat modeling is — and is not
Threat modeling is a structured way to find security problems in a design before that design becomes running code. You look at what you intend to build, reason adversarially about how it could be abused, decide what to do about each abuse, and check whether you did enough. The output is not a green checkmark — it is a list: threats you found, mitigations you chose, and assumptions you are now consciously relying on. That list is a living artifact that travels with the feature.
It helps to be precise about what threat modeling is not, because each confusion leads teams to skip it. It is not a tool you run; no scanner produces a threat model, because the interesting threats live in intent and architecture, which no static analyzer can read. It is not a one-time gate you clear before launch; a design changes, and the model changes with it. It is not the same as a risk-acceptance signoff or a compliance checklist — those record decisions, while threat modeling generates the decisions to be recorded. And it is emphatically not pentesting. We’ll make that contrast sharp below, because it is the single most useful distinction a fullstack engineer can internalize: threat modeling is design-before, pentesting is find-after.
The four questions
The discipline collapses to four questions, popularized by Adam Shostack and adopted almost verbatim by the Threat Modeling Manifesto. They are deceptively plain, and their order matters.
- What are we building? You cannot threaten a system you cannot see. This is where you draw the thing — typically a data-flow diagram (DFD): processes, data stores, external entities, the flows between them, and the trust boundaries they cross. A trust boundary is any place where data or control passes between zones of differing trust — browser to server, your service to a third-party API, one microservice to another. Threats cluster on boundaries, so naming them well is most of the work.
- What can go wrong? Now you reason adversarially, boundary by boundary. This is where structured prompts earn their keep — STRIDE (Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege) gives six lenses to walk each element through so you don’t only imagine the attacks you already fear. The product of this step is a set of abuse cases: the mirror image of user stories. “As a user I can share a document” gets paired with “as an attacker I can escalate a viewer link to an editor link.”
- What are we going to do about it? Each plausible threat gets a disposition: mitigate (add a control), eliminate (remove the feature or the boundary), transfer (push the risk to a provider with stronger guarantees), or accept (consciously, with the assumption written down). “Accept” is a legitimate answer — but only when it is recorded, so it is a decision and not an accident.
- Did we do a good enough job? You review the model against the design as built, confirm the mitigations landed, and decide whether the residual risk is tolerable. Crucially, the loop closes back to question one: the mitigations you chose change the design, which can introduce new boundaries and new threats.
Where it lives in the SDLC, and who is in the room
The whole value proposition rests on when this happens: at design time, before code. This is what people mean by shift-left — moving security analysis leftward on the timeline toward requirements and design, away from the right edge where you find problems in production. You threat-model when the feature is a diagram and a doc, because that is the moment when “we shouldn’t build it that way” is a cheap sentence rather than a re-architecture. Practically, that means in the design review for a new feature, at an architecture change, or when a trust boundary moves (a new third-party integration, a new tenant model, exposing an internal service).
Who is in the room matters more than which methodology you pick. The engineers who will build it must be there — they know the real data flows, not the idealized ones. A security-literate facilitator keeps the adversarial questions flowing. Product or domain owners define what “abuse” even means (in payments, double-spend; in healthcare, unauthorized record access). The Manifesto is blunt here: it values people and collaboration over processes, methodologies, and tools. A whiteboard session with the right four people beats an elaborate tool used by one person in isolation.
▸Why this works
Why a list and not a score? A single risk score (“this feature is 7/10 risky”) compresses away the only thing that’s actionable: which threat, mitigated how, under what assumption. The threat model’s real output is three coupled lists — threats found, mitigations chosen, assumptions relied on — because those are what you hand to the implementer, the reviewer, and your future self when the design changes. The assumptions list is the sleeper: “we assume the internal network is trusted” is fine until someone exposes that service to the internet and nobody re-checks the assumption that quietly became false.
Threat modeling vs pentesting: design-before vs find-after
These are complementary, not interchangeable, and conflating them is how teams end up with neither. A pentest is a time-boxed adversarial assessment of a running system: skilled testers probe the deployed application and report exploitable findings. It is empirical, concrete, and authoritative about what is exploitable today — and it happens after the system exists. Threat modeling is analytical and happens before: it reasons about a design to find classes of problem, including ones a pentest can never surface because they are not bugs but missing requirements.
That last point is the crux. A pentester can find a SQL injection because the injectable endpoint exists to probe. A pentester usually cannot tell you that your share-link design has no concept of per-link permission scoping — because from the outside it looks like the system working as designed. That flaw is invisible to find-after testing precisely because it is a design decision, not a defect. You catch it only by asking “what can go wrong?” while the design is still a diagram. Use both: model the design before you build it, pentest the result after you ship it.
| Dimension | Threat modeling | Pentesting |
|---|---|---|
| When | Design time, before code | After the system runs |
| Object | A design / diagram | A deployed application |
| Mode | Analytical, adversarial reasoning | Empirical, hands-on exploitation |
| Catches | Design flaws + missing requirements | Exploitable implementation bugs |
| Output | Threats + mitigations + assumptions | Ranked list of findings to fix |
| Blind spot | Bugs in code it never reviews | Flaws that look like intended behavior |
The economics: why finding it early is the whole point
Threat modeling is not free. A worthwhile session costs an hour or two of several senior people, plus the discipline to keep the model alive as the design moves — call it real engineering time per non-trivial feature. The case for paying it is the cost asymmetry of not paying it. The long-cited industry rule of thumb, traced back to research summarized in NIST and IBM Systems Sciences Institute figures, is that a defect costs on the order of 1 unit to fix at design, ~6.5x at implementation, ~15x at testing, and 30–100x once it reaches production. The exact multipliers are debated and vary by study, but the shape is robust and intuitive: the later a flaw is found, the more code, data, and downstream decisions have calcified around it.
For a design flaw the asymmetry is worse than for an ordinary bug, because the late fix is not a one-line patch — it is a re-architecture, often a data migration, sometimes a coordinated change across services that already trust the broken assumption. The failure mode to fear is the one in the hook: you skip the modeling, ship a design whose flaw cannot be patched (the missing permission scope, the client-trusted price, the unauthenticated internal endpoint that just got exposed), and now you are paying the 30–100x multiplier and re-architecting and doing it under disclosure or incident pressure. The tradeoff is stark precisely because it is so lopsided: a couple of senior-hours of structured pessimism at the whiteboard buys you out of a class of the most expensive failures in software.
Your team is about to build the document-sharing feature from the hook (share by signed link). You have one design review on the calendar. What is the highest-leverage security move?
Why can a pentest miss the share-link permission flaw that threat modeling would catch?
A teammate says 'we'll skip threat modeling and just fix anything the pentest finds before launch — it's cheaper.' What's the flaw in that reasoning?
Order the four threat-modeling questions as you'd work them in a design review:
- 1 What are we building? (draw the DFD, mark trust boundaries)
- 2 What can go wrong? (STRIDE, abuse cases per boundary)
- 3 What are we going to do about it? (mitigate / eliminate / transfer / accept)
- 4 Did we do a good enough job? (review residual risk, loop back to the design)
- 01State the four questions of threat modeling, in order, and say why the order and the loop matter.
- 02Contrast threat modeling with pentesting, and explain the cost asymmetry that justifies doing the modeling.
Threat modeling is a structured, design-time activity that answers four questions — what are we building, what can go wrong, what are we going to do about it, and did we do a good enough job — and produces a living list of threats, mitigations, and assumptions, not a pass/fail score. You draw the system as a data-flow diagram, mark the trust boundaries where threats cluster, walk each boundary with prompts like STRIDE to generate abuse cases that mirror your user stories, disposition each threat (mitigate, eliminate, transfer, or consciously accept), and then loop back because mitigations change the design. It is not a scanner, not a one-time gate, and not pentesting: a pentest is empirical and runs after the system exists, so it catches exploitable bugs but structurally misses the missing-requirement design flaws that look like intended behavior. The right people in the room — the engineers who’ll build it, a security-literate facilitator, a domain owner — beat any tool, which is why the Manifesto values people and collaboration over process. The economics close the case: a defect costs ~1x at design and 30–100x in production, and a design flaw found late is a re-architecture rather than a patch — so the next time a whiteboard says “users can share a link”, your reflex is to ask, before anyone writes code, what can go wrong here?
Practice
Start at the top. Tasks go easiest → hardest: recall a fact, apply it to a case, then a senior-level stretch. Open one, attempt it, then reveal.
Something unclear?
Ask a question about this lesson. Questions are anonymous and go straight to the author to make the lesson better.
Apply this
Put this lesson to work on a real build.