Threat Modeling: Think Like the Attacker
What do you have that's valuable, who wants it, and where does your code trust data it shouldn't?
Security is not a feature you add; it's a property you avoid losing. Start every design with three questions.
The four questions
- What are we building? (a feature that accepts comments, uploads, payments...)
- What can go wrong? (the classic STRIDE categories: Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege)
- What will we do about it? (prevent, detect, mitigate, accept — explicitly)
- Did we do a good job? (tests, reviews, audits)
You don't need the vocabulary to need the discipline: "the comment field ends up in HTML — what if it contains a script tag?" is threat modeling.
Trust boundaries
Draw the line where data crosses: user input → server, server → database, database → HTML, third-party API → your code. Data inside a boundary is (relatively) trustworthy; data crossing IN is hostile until proven otherwise. Every XSS, SQLi, and path traversal bug is data crossing a boundary without validation or encoding.
The attacker's cheapest wins
Professional attackers don't hack; they check for the known-open doors:
- Reused/leaked credentials (module 6's secrets lesson!)
- Missing authorization checks (the IDOR:
/api/invoices/4821works for invoices that aren't yours) - Unsanitized output (XSS)
- Old dependencies with published CVEs
Defense is mostly not being the low-hanging fruit: validate input, encode output, check ownership on every read, patch dependencies, and never roll your own crypto.
The defender's stance (this course's stance)
We practice defensive security only: recognize → understand → prevent → safely verify. Every exercise in this module is "here is a vulnerable pattern; find it, explain it, fix it" — never "here is how to exploit someone else's site," which is illegal as well as unhelpful.