Threat Modeling: Thinking Like an Attacker

Beginner
11 min

Threat Modeling: Thinking Like an Attacker

Most security bugs happen because nobody asked "what could go wrong here?" before the code was written. Threat modeling is the structured habit of asking that question early, when the fix costs a whiteboard sketch instead of an emergency release.

In this lesson you will learn the four questions every threat model answers, how to draw a data flow diagram with trust boundaries, how to enumerate threats with STRIDE, and how to turn results into tickets.

The Four Questions

A threat model needs no special tool. It needs four questions answered honestly:

  1. What are we working on? Components, data, and who talks to whom.
  2. What can go wrong? Threats against each component and connection.
  3. What are we going to do about it? A control for each threat, or an explicit decision to accept the risk.
  4. Did we do a good job? Review after the feature ships and whenever the design changes.

The output is a short living document (docs/threat-model.md) updated in the same pull request that changes the architecture.

Step 1: Draw the Data Flow

A data flow diagram (DFD) uses four shapes: external entities (users, third parties), processes (your API), data stores (database, cache), and arrows between them. Then add trust boundaries wherever data crosses from one level of trust to another.

text
[Browser] --HTTPS--> | [API server] --TCP--> | [PostgreSQL] untrusted | trusted | internal | | | +--HTTPS--> | [Payment gateway] | | third party

Every arrow crossing a boundary is a place where input must be validated, identity checked, and secrets protected. This sketch has three such crossings.

Step 2: Enumerate Threats with STRIDE

STRIDE lists six ways a system can be attacked. Walk through it for every element of the diagram.

| Threat | Question to ask | Typical control | |---|---|---| | Spoofing | Can someone impersonate a user or service? | Authentication, MFA, signed tokens | | Tampering | Can data be modified in transit or at rest? | TLS, server-side validation | | Repudiation | Can a user deny an action? | Audit logs with identity and time | | Information disclosure | Can data leak to the wrong party? | Encryption, access control, terse errors | | Denial of service | Can the system be made unavailable? | Rate limiting, quotas, timeouts | | Elevation of privilege | Can a user gain extra rights? | Authorization on every request |

Applied to the "Browser to API" arrow this yields concrete findings: a stolen cookie (S), a modified price field (T), no record of who cancelled an order (R), a stack trace in a 500 response (I), an unpaginated search endpoint (D), an /admin route without a role check (E).

Step 3: Rate and Prioritise

You will find more threats than one sprint can fix. Rate likelihood and impact from 1 to 3 and multiply:

javascript
const findings = [ { threat: "Admin route without role check", l: 3, i: 3 }, { threat: "Stack trace in error page", l: 3, i: 2 }, { threat: "No audit log for refunds", l: 1, i: 2 }, ].map(f => ({ ...f, score: f.l * f.i })) .sort((a, b) => b.score - a.score); console.table(findings); // scores 9, 6, 2

For each finding record one decision: mitigate (add a control), eliminate (remove the feature or data), transfer (a payment provider stores card numbers instead of you), or accept (document why the risk is tolerable). Then convert every "mitigate" into a ticket with an acceptance test:

markdown
### Ticket: Enforce admin role on /admin/* routes Threat: Elevation of privilege (STRIDE-E), score 9 Control: requireRole("admin") middleware on the admin router Acceptance test: GET /admin/users as a normal user returns 403

The acceptance test makes CI verify the control forever.

Tips

  • Model per feature: a 20-minute session when a new endpoint is designed beats a yearly workshop.
  • Invite one person who did not build the feature; fresh eyes find hidden assumptions.
  • Keep the assets list short: credentials, personal data, money, anything with legal weight.
Quick Quiz
Question 1 of 2

Which STRIDE category describes a user modifying the `price` field in a checkout request?

Key Takeaways

  • Threat modeling answers four questions: what are we building, what can go wrong, what will we do, did we do a good job.
  • A data flow diagram with trust boundaries shows where validation and authentication must happen.
  • STRIDE is the checklist for enumerating threats against each element.
  • Score threats by likelihood times impact, then mitigate, eliminate, transfer or accept each one.
  • Every mitigation becomes a ticket with an acceptance test that CI verifies.

Next lesson: Social Engineering Defenses for Developers — how attackers target engineers specifically, and the habits that stop them.

Threat Modeling: Thinking Like an Attacker - Cyber Security | CodeYourCraft | CodeYourCraft