Skip to content

Threat Modeling & Abuse Prevention

You can’t secure a system you haven’t reasoned about. Threat modeling is the discipline of asking, before you ship: who would attack this, what are they after, where are the weak points, and what do we do about each? It turns security from a vague worry into a checklist tied to your actual design.

A trust boundary is any line where data crosses from a less-trusted zone to a more-trusted one — and it’s exactly where attacks happen.

[ user's browser ] ──trust boundary──► [ your API ] ──trust boundary──► [ your database ]
untrusted input VALIDATE internal PARAMETERIZE queries

The rule that follows: never trust input that crossed a boundary. Validate and sanitize at every crossing. The classic failures — SQL injection, XSS — are all “trusted some input that came from an untrusted zone.” Drawing the boundaries on your architecture diagram is step one of any threat model.

STRIDE: a checklist for “what could go wrong”

Section titled “STRIDE: a checklist for “what could go wrong””

STRIDE is a mnemonic that prompts you to consider six categories of threat at each component and boundary:

ThreatQuestionDefense (examples)
SpoofingCan someone pretend to be another identity?Authentication, MFA
TamperingCan data be modified in transit/at rest?Encryption & TLS, signatures
RepudiationCan someone deny an action they took?Audit logging, signed records
Information disclosureCan data leak?Encryption, least-privilege authorization
Denial of serviceCan it be overwhelmed?Rate limiting, autoscaling, CDN
Elevation of privilegeCan a user gain rights they shouldn’t?Authorization checks, sandboxing

Walk each component through these six and you’ll surface most realistic threats systematically rather than hoping you thought of everything.

A worked threat model: the password-reset endpoint

Section titled “A worked threat model: the password-reset endpoint”

Checklists click once you actually run one. Take POST /password-reset — a deceptively dangerous endpoint — and walk it across its trust boundary:

[ anyone ]──trust boundary──►[ reset API ]──►[ email service ] [ user DB ]
submits an email generates a token, emails a reset link

Now run STRIDE against that one boundary:

STRIDEConcrete threat hereMitigation that falls out
SpoofingRequest a reset for someone else’s emailToken sent only to the registered address
TamperingGuess or alter the reset tokenLong, random, single-use token; server-side check
Repudiation”I never requested this”Log every request with IP + timestamp
Info disclosureDifferent response for real vs unknown email → account enumerationIdentical response and timing either way
DoSFlood resets to spam a victim / burn email quotaRate-limit per email and per IP
ElevationOld link still works → account takeoverShort TTL, invalidate token on use

The payoff: those mitigations weren’t memorized — they fell out of running one method against one boundary. That is what threat modeling actually buys you.

Most application-layer compromises trace back to trusting input:

  • SQL injection → use parameterized queries, never string-concatenated SQL (see Transactions & ACID’s data layer).
  • XSS → escape/encode output, use Content Security Policy.
  • Oversized / malformed payloads → enforce size and schema limits at the edge.

The principle is uniform: allowlist what’s valid, reject everything else — far safer than trying to blocklist every bad pattern.

A Denial-of-Service attack tries to exhaust a resource (CPU, connections, bandwidth, a database connection pool) so legitimate users can’t get through. Distributed DoS does it from many sources at once. Layered defenses:

[ Anycast / CDN / scrubbing ] absorb volumetric floods at the edge (far from origin)
[ Rate limiting / WAF ] cap per-client request rate; block known-bad patterns
[ Autoscaling + load shedding ] grow under load; shed excess to protect the core
[ Your service ] smallest, last line — never the first

Two very different DDoS shapes need different defenses:

  • Volumetric (Gbps–Tbps of junk — UDP/amplification floods): absorbed at the edge by Anycast + scrubbing capacity larger than the attacker’s. Your origin never sees it.
  • Application-layer (L7): low volume but expensive requests (a search scanning millions of rows, a costly report endpoint). A CDN won’t catch these — they look like real traffic — so you defend with per-endpoint rate limits, query budgets, and shedding the most expensive paths first.

Rate limiting is the workhorse for abuse (credential stuffing, scraping, brute force) as well as DoS — it caps how fast any one actor can act. Pair it with graceful degradation and load shedding so an overload sheds the excess instead of collapsing entirely.

Credential stuffing is the most common login abuse and worth understanding mechanically: attackers replay username/password pairs leaked from other breaches, betting on password reuse. Rate limiting alone is weak (the attack is spread across many IPs and accounts), so you layer defenses — breached-password checks, device fingerprinting, MFA, and login-velocity anomaly detection. A WAF matches request signatures but can’t tell that a perfectly valid-looking login is fraudulent — which is exactly why abuse defense is layered, not a single appliance.

No single control is enough; assume each layer can fail and stack independent defenses (network → edge → app → data). The goal is that breaching one layer doesn’t hand over the system — it just forces the attacker to defeat the next one.

What does this buy us, and what does it cost? Threat modeling and abuse prevention cost design time, some latency (validation, rate-limit checks), and infrastructure (WAF, scrubbing). They buy a system whose failure modes you’ve anticipated rather than discovered in an incident. Security is fundamentally about trust boundaries and least privilege — decide explicitly what you trust, verify everything that crosses a line, and limit what any one breach can reach.

  1. What is a trust boundary, and what universal rule applies at every one?
  2. Name the six STRIDE categories and give a defense for any three.
  3. Why is allowlisting valid input safer than blocklisting bad input?
  4. Describe the layered defenses against a DDoS attack, from edge to origin.
  5. Why should you design as if clients will misbehave even absent a malicious attacker?