Threat modelling in an hour, with four questions and a whiteboard
Threat modelling has a reputation for heavyweight documents nobody reads. The useful version is a one-hour conversation over a diagram, answering four questions, producing a short list of decisions — and it catches the class of flaw that no scanner will ever report, because the code is working exactly as written.
Automated tooling finds implementation bugs: an injectable query, a vulnerable dependency, a missing header. It cannot find design flaws, because a design flaw is code doing precisely what it was asked to do. An endpoint that returns any record when given its identifier is not a bug in any scanner's model — it is a missing authorisation decision, and only a person reasoning about the design will notice.
The four questions
- What are we building? Draw it. Not the class diagram — the data flows, the trust boundaries, and where data is stored.
- What can go wrong? Walk the boundaries systematically rather than brainstorming freely.
- What are we going to do about it? Every finding gets one of four dispositions, with a name attached.
- Did we do a good job? Revisit when the design changes, and check whether the mitigations were actually built.
That framing comes from the threat-modelling community and it is deliberately unsophisticated. Its value is that a mixed group — a backend engineer, someone who knows the product, someone who knows the infrastructure — can work through it without prior training.
Draw the trust boundaries, not the architecture
Most architecture diagrams are useless for this because they show components rather than the lines data crosses. A threat-model diagram needs exactly four things: external actors, processes, data stores, and boundaries between zones of differing trust.
STRIDE, per boundary crossing
Free-form brainstorming finds whatever the loudest person in the room is worried about. A checklist finds the categories nobody thought of. STRIDE is the standard one, and running it per arrow rather than per system is what makes it tractable.
In practice two categories produce most of the findings. Information disclosure catches the endpoint that trusts an identifier from the client; elevation of privilege catches the check performed in the interface but not on the server. Both are design flaws, and both are invisible to scanners.
Four dispositions, each with a name attached
A finding with no disposition becomes a line in a document nobody revisits. Force one of four, and record who decided.
Accepting risk is legitimate and underused — plenty of findings genuinely are not worth the control. What is not legitimate is accepting it implicitly. An accepted risk with a name and a date is a decision; an unassigned one is an oversight waiting to be discovered by someone else.
Eliminate deserves more attention than it gets. A field you stop collecting cannot leak, and a feature you remove needs no control. Data minimisation is the strongest available security measure and it also reduces obligations under data-protection law.
When to run one
- New service or major feature — before implementation, while the design is still cheap to change
- New trust boundary — a new third party, a new integration, a new class of user
- Handling a new category of data — payments, health, biometric, anything that changes the obligations
- After an incident — model the neighbourhood of what happened, not only the specific fault
- Annually for critical paths — because the system changed even where the diagram did not
What the output should look like
A page. The diagram, and a table of findings with dispositions and owners. Anything longer will not be read, and anything shorter loses the reasoning that made the decisions defensible.
Finding 5 in that example is the one worth noticing. Taking the tenant identifier from the request body rather than the session is a single line of code and a complete cross-tenant data breach, and it is exactly the kind of thing an hour with a diagram catches while a test suite happily passes.
Questions
How long should a threat model take?
One hour for a feature, two or three for a new service. The lightweight version exists because a session that fits in an hour actually happens before implementation, which is where its value lies. A comprehensive model produced after launch generates a backlog rather than changing a design.
Why not rely on scanners and penetration tests?
They find different things. Scanners find implementation bugs — injectable queries, vulnerable dependencies, missing headers. A design flaw is code working exactly as specified, so no scanner will report it. An endpoint that returns any record when given its identifier has no bug in it; it has a missing authorisation decision, and only a person reasoning about the design will notice.
Do we need a security specialist in the room?
It helps and it is not a prerequisite. The four questions and the STRIDE checklist are designed to be workable by a mixed engineering group. What matters more is having someone who understands the data flows, someone who understands the infrastructure, and a willingness to write down accepted risks with a name against them.
What is the most common finding in practice?
An endpoint that accepts an identifier from the client and returns the corresponding record without checking that this caller is entitled to it. Tests pass because test users request their own records. The related pattern is deriving a tenant or organisation identifier from the request body rather than the session, which turns one line of code into a cross-tenant breach.