There is a category of security problem that no scanner will ever find. Not because the tooling is inadequate, but because the code is doing exactly what it was designed to do — and the design is the problem.

A password reset flow that emails a token but never binds it to a session is not a bug. Every line works as intended. The flaw is architectural, and the only way to catch it is to reason about the design deliberately before it becomes an implementation. That is threat modelling.

Four questions

Strip away the frameworks and the practice reduces to four questions, framed by Adam Shostack and widely adopted since:

  1. What are we building?
  2. What can go wrong?
  3. What are we going to do about it?
  4. Did we do a good job?

Everything else is technique for answering these more reliably. Teams that skip the first question produce threat models describing a system nobody actually built; teams that skip the fourth never learn whether the exercise was worth the afternoon.

Start with a diagram that shows trust boundaries

The prerequisite is a shared picture of the system, and the useful abstraction is a data flow diagram: the external entities, the processes, the data stores, the flows between them — and, critically, the trust boundaries those flows cross.

A trust boundary is any point where the privilege or trustworthiness of data changes: browser to server, application to database, your service to a third-party API, one tenant to another. They matter because virtually every interesting vulnerability lives on one. Data that stays entirely within a single trust level is rarely where the problem is.

Keep the diagram at the level where architectural decisions are visible. A whiteboard sketch that everyone in the room agrees is accurate beats a beautiful diagram that is six months stale.

STRIDE, and what each letter is really asking

STRIDE gives structure to the second question by pairing each threat category with the security property it violates. Applied per element and per flow, it turns an open-ended brainstorm into something systematic.

The value of the mnemonic is coverage. Left unstructured, teams find the threats they already expect; STRIDE forces attention onto categories that would otherwise be skipped — repudiation in particular is almost never raised spontaneously.

A short worked example

Consider a password reset. The flow: user requests reset, service emails a link containing a token, user follows it, service accepts a new password.

Applied systematically, the questions become concrete. Spoofing: can someone request a reset for another account, and does the response reveal whether that account exists? Tampering: is the token bound to the specific account, or can it be altered to apply elsewhere? Information disclosure: does the token appear in a URL, where it will land in browser history, server logs and any referrer sent to a third party on that page? Denial of service: can the endpoint be used to flood a mailbox, or to burn a target’s ability to reset at all? Elevation of privilege: does completing a reset invalidate existing sessions, or can an attacker who already holds a session retain it after the legitimate owner recovers the account?

That last one is the flaw that opened this article, and it is invisible to every scanner. It is also entirely obvious once someone asks the question out loud.

// try it live

Tampering questions frequently come down to what a token actually asserts, and whether the design trusts it too readily.

JWT Decoder CVSS Calculator

Deciding what to do

Every identified threat resolves into one of four responses, and naming the choice explicitly matters as much as making it.

Prioritise by exploitability and impact rather than by category. A theoretical elevation of privilege requiring physical access matters less than an information disclosure reachable anonymously over the internet.

Making it happen in practice

The reason threat modelling is widely endorsed and narrowly practised is that heavyweight versions collapse under their own weight. A few things make it stick:

Where it fits alongside testing

Threat modelling does not replace static analysis, dependency scanning or penetration testing. It addresses a different failure mode entirely. Scanners find implementation defects in code that exists. Threat modelling finds design defects before the code exists, when changing the design is still cheap.

That economic point is the whole argument. Restructuring an authorisation model during design costs a conversation. Restructuring it after launch costs a migration, a regression risk and an incident write-up. The practice earns its place not because it finds more issues than automated tooling, but because it finds the ones automation structurally cannot — at the only moment when they are still inexpensive to fix.

A catalogue of design flaws worth checking for

Experience makes threat modelling faster because the same design mistakes recur across systems. Working through a checklist of known patterns is a reasonable substitute for that experience while a team builds it.

Authentication and session design

Does authentication state carry any client-controlled component? Are tokens invalidated on password change, role change and logout? Can a session be fixated before login? Is there a path that issues a session without full authentication — a partially authenticated state during MFA, for example, that grants more than it should?

Authorisation placement

Is authorisation enforced at a single choke point or repeated in every handler? The latter guarantees an inconsistency eventually. Are checks performed on the object being accessed, or only on the endpoint being called? Does any code path retrieve an object first and check permission afterwards, leaking existence through timing or error differences?

Data handling

Where does sensitive data come to rest that nobody intended — logs, caches, error reports, analytics, backups? Is data classified so that decisions about encryption and retention have something to key on? Are identifiers used in URLs sequential and therefore enumerable?

Trust in the client

Any value computed client-side and accepted server-side is a design flaw regardless of how it is transmitted. Prices, quantities, role claims, feature flags, and validation results all belong here.

Third-party integration

What can the third party do if compromised? Are webhook callers authenticated, or does the endpoint accept any well-formed request? Does the integration hold broader permissions than its function requires?

Modelling systems that already exist

Most threat modelling in practice is applied to systems already in production, where the design decisions were made years ago by people who have left. The method adapts, with two adjustments.

First, build the diagram from reality rather than from documentation. Infrastructure inventories, network configuration, identity role assignments and actual traffic tell you what the system is; architecture documents tell you what it was intended to be, and the gap is frequently where the risk sits.

Second, prioritise ruthlessly. A legacy system will generate more findings than anyone can remediate, and producing a hundred-item list that nobody actions is worse than useless because it creates the appearance of diligence. Focus on the trust boundaries carrying the most sensitive data and the paths reachable by the least-trusted actors, and accept the rest explicitly with a written rationale.

Scaling it beyond one team

Threat modelling fails to scale when it depends on a small central security team attending every session, which becomes a bottleneck and then a rubber stamp. The alternative is to distribute the practice and keep the expertise for the hard cases.

What tends to work: a lightweight template teams complete themselves; a short list of triggering events that require a model rather than a blanket policy; a security champion embedded in each team who has done it several times; and central review reserved for systems handling the most sensitive data or introducing genuinely novel architecture.

Measure adoption by whether findings become tickets and tickets become fixes, not by how many models exist. A modest model that changed one design decision is worth more than a comprehensive one filed and forgotten.

References

  1. OWASP — Threat Modeling
  2. OWASP — Threat Model Project
  3. Microsoft — Threat Modeling Tool: STRIDE threat categories
  4. NIST SP 800-218 — Secure Software Development Framework
« Network Detection: What DNS, Proxy… API Security: Why the OWASP… »