A threat model in 15 minutes

What you protect, from whom, with what.

6 mineasy

Before you defend against anything, write down what you're protecting, from whom, and how badly each thing matters. That is a threat model. It takes 15 minutes, costs nothing, and is the single highest-leverage security activity for a small team.

What a threat model answers

  • What are we protecting? User data? Payment info? Source code? IP?
  • From whom? Script kiddies? Competitors? Disgruntled employees? Nation-states?
  • What are we willing to spend? A hobby project and a fintech have very different budgets.
  • What's the worst case? A data leak embarrasses you; a payment leak ends you.

STRIDE — the canonical mental model

STRIDE categories

S - Spoofing identity (logging in as someone else) T - Tampering with data (changing data you shouldn't) R - Repudiation (denying you did something) I - Information disclosure (leaking data) D - Denial of service (crashing the service) E - Elevation of privilege (becoming admin)
A starter taxonomy. Walk every endpoint and ask: which of these applies?

For each endpoint in your API, ask: which letters apply? Most endpoints have a few. A login endpoint cares about S, I, D. A notes endpoint cares about T, I, E. Knowing which letter applies focuses your defence.

The shape of the document

A threat model doesn't need to be a 50-page Word doc. Yours can be a single Markdown file. Three sections:

1. Assets — what you protect

  • User accounts (email, password hash, profile)
  • Tenant data (orders, notes, files)
  • Payment info (last 4, never the full card)
  • Admin access
  • Service availability

2. Actors — who you protect against

  • Anonymous user (public internet)
  • Authenticated regular user (logged in)
  • Authenticated user trying to access another user's data
  • Compromised admin account
  • Insider (developer, employee)

3. Threats — what could go wrong

One line per threat. Be specific. Map each to STRIDE.

  • T1 (I, T): A logged-in user PATCHes another user's note by guessing the ID. (IDOR)
  • T2 (S): An attacker uses a leaked JWT secret to forge tokens for any user.
  • T3 (D): Bot floods /api/auth/login with brute force; password reset queue grows; legit users locked out.
  • T4 (E): SQL injection on a search endpoint exfiltrates the users table.
  • T5 (I): Public bucket lets anyone download customer uploads.

Risk = likelihood × impact

Rate each threat 1-5 on likelihood and 1-5 on impact. Multiply. Sort. Spend your time on the top half. Ignore the bottom half for now.

Don't obsess over precision. The point is forcing yourself to compare threats so you don't spend a week on a 1×1 threat while a 5×5 sits unhandled.

You will be wrong about likelihoods, often. That's OK. The act of writing it down + revisiting it quarterly is the value. Treat the model as a living draft.

The smallest model that's useful

For a small team / solo founder, a useful threat model fits on one page:

  • 5 assets
  • 5 actors
  • 10 threats, ranked
  • The top 3 actively being mitigated

Anything more is overkill until you have customer/regulatory pressure for one.

When to revisit

  • You launched a major feature (new endpoints, new data).
  • You added a new third-party service (more attack surface).
  • You had a security incident or scare.
  • It's been > 6 months and you haven't looked.

Quick check

A solo founder building an MVP asks: 'Should I do a threat model now, or after launch?'. What's the best advice?

Try it

Write a threat model for your own Grit API:

  1. Create SECURITY.md at your repo root.
  2. List 5 assets you protect (be specific to YOUR product).
  3. List 5 actors.
  4. List 10 threats — one per STRIDE category at minimum. Rate likelihood (1-5) × impact (1-5).
  5. Sort. Underline the top 3.
  6. For each of the top 3, write 1 sentence on the planned mitigation.

What's next

Next lesson — OWASP Top 10 tour. One sentence per category, mapped to real Grit endpoints. Then we attack IDOR in chapter 2.

Spot a typo? Have an idea?

Help us improve this lesson. One click opens a GitHub issue with the lesson URL pre-filled — suggest clearer wording, report a bug, or request more depth. The course keeps improving thanks to learners like you.

Suggest an improvement on GitHub