System Design · Security
Threat Modeling for System Design
“Design a URL shortener” is incomplete until someone asks who can mint a redirect to malware, enumerate private slugs, or melt the hot key. A is that conversation, written down.
Draw a with , walk , grow an for the crown-jewel goal, then attach and honest .
Modeling journey
Links, keys, reputation, capacity.
Flows and stores, then boundaries.
Walk each element once.
Cheapest path to the goal.
What actually happens
Start from and actors, not from tools. A DFD without the click analytics store will miss disclosure of browsing history. Every cross-boundary flow is a candidate for spoofing or tampering.
is a checklist on those elements. An is better when you already know the attacker’s goal (take over redirects, ransom availability). keep both tied to product language.
Heavier methods (, for privacy) belong when the business risk is high. Interview answers should still be STRIDE + tree + residual risk in ten minutes, fed by (“credential stuffing is real on this login”).
How to run a design-time model
1. Scope. One user journey (create link, follow link, admin disable). Unbounded whole-company models never finish.
2. Diagram. Processes, stores, externals, data flows. Circle trust boundaries. Delete decorative boxes.
3. Threaten, then mitigate. STRIDE cells, tree for the top goal, map each high-likelihood path to a control, owner, and metric. Record residual risk in the same doc.
Simple rule of thumb
If you cannot name the cheapest path to the worst asset, you do not have a threat model yet—you have a architecture poster.
Data-flow threats
Boundaries are where STRIDE earns its keep
Read the diagram definition
flowchart LR
Browser["Browser"] -->|HTTPS| Edge["Edge / WAF"]
Edge -->|mTLS| API["URL API"]
API --> Cache["Cache"]
API --> DB["Link store"]
API --> Queue["Click events"]
Queue --> Analytics["Analytics"]
Admin["Admin SSO"] --> API
subgraph TB1["Trust boundary: internet"]
Browser
end
subgraph TB2["Trust boundary: control plane"]
Admin
end
subgraph TB3["Trust boundary: data"]
DB
Analytics
end| Element | S | T | R | I | D | E |
|---|---|---|---|---|---|---|
| Browser → edge | Fake Host / stolen cookie | TLS strip if you allow HTTP | Shared NAT IPs | Referrer leaks | Volumetric + L7 | — |
| API process | Forged internal header | IDOR update target | No actor on writes | Verbose errors | Hot slug | Debug endpoint |
| Link store | Stolen DB role | Row rewrite | Deletes without audit | Dump of private slugs | Lock contention | OVER_GRANT role |
| Analytics store | Job identity reuse | Poisoned events | Unsigned pipeline | Click trails | Unbounded fan-out | Warehouse SA → OLTP |
Data-flow threats include metadata: slug length, timing of follows, and cache hit ratio can leak that a private campaign exists even when the target URL is encrypted at rest.
STRIDE in interview English
One sentence each, then a control
S · Spoofing
Prove identity: mTLS, SSO, bound cookies. Never trust X-User-Id from the client.
T · Tampering
Integrity: TLS, signed payloads, optimistic concurrency on link records.
R · Repudiation
Accounting: immutable audit with actor, resource, reason—not just NAT IP.
I · Info disclosure
Unguessable slugs, no sequential IDs, field encryption, error hygiene.
D · Denial of service
Per-slug and per-IP budgets; cache popular keys; cap origin RPS.
E · Elevation
No debug listeners; workers cannot write the link table; admin is JIT.
Attack trees
AND/OR paths beat a flat bug list
Read the diagram definition
flowchart TB
G["Goal: steal or hijack short links"]
G --> S["Spoof admin session"]
G --> T["Tamper with redirect target"]
G --> R["Repudiate abusive clicks"]
G --> I["Info: enumerate private slugs"]
G --> D["DoS the lookup path"]
G --> E["Elevate: worker writes DB"]
S --> S1["Phish SSO"]
S --> S2["Steal cookie on XSS"]
T --> T1["IDOR on update API"]
T --> T2["Poison cache entry"]
I --> I1["Sequential slug brute force"]
D --> D1["Hot-key flood on popular slug"]AND vs OR nodes
Annotate cost, not only possibility
From model to design changes
Controls, detection, residual risk
Worked shortener controls
- Random 64+ bit slugs; rate-limit create and lookup independently.
- Updates require owner or admin; IDOR tests in CI.
- Cache keys include slug + target version so poison is versioned out.
- Click pipeline is append-only and cannot UPDATE links.
Detection and residual
- Alert on target-URL changes, bulk creates, and origin RPS vs cache hit.
- Residual: a phished admin still redirects until session revoke SLO fires.
- Residual: metadata of private slugs if length and timing leak.
Sizing: 10k create/sec with 8-byte slugs is not the security problem; sequential 6-character base62 (~56B space) at 100k guesses/sec is enumerable in days. That one number should change the ID design before you discuss Kafka.
Trade-offs
Breadth vs finishability. Whole-estate PASTA never ships. One journey STRIDE does. Time-box, then iterate when intel changes.
Checklist vs trees. STRIDE avoids forgetting DoS. Trees show combinations. Use both; do not score STRIDE cells as if they were CVSS.
Prevent vs detect. Blocking unguessable IDs is cheap. Detecting a phished admin needs session analytics. Budget both.
Privacy vs security STRIDE. LINDDUN finds linkability the CIA triad under-weights. Dual-running both on the same DFD is extra work and extra truth.
Accepted residual vs theater. Writing “risk accepted” without an owner is how models become wallpaper.
What must never become false
Every flow that crosses a trust boundary must have a named spoofing and tampering story, and the cheapest path to the highest-value asset must either be mitigated or explicitly accepted with an owner. A diagram without that path is not a threat model.
Failure timeline: model skipped the store
T+0 Whiteboard STRIDE on the HTTPS API only T+2w Sequential slugs ship; "we'll add auth later" T+1m Attacker enumerates campaign links; disclosure (I) never had a cell T+1m+1d Analytics job uses the API role; dumps click trails T+1m+2d Hot celebrity slug stampede; origin has no per-key budget (D) Outcome The DFD omitted stores and the tree omitted cheap leaves
Exercises
- Draw a level-1 DFD for create/follow/admin-disable. List one STRIDE hit per boundary crossing and one residual risk you would accept at v1.
- Build an attack tree for “replace a bank’s short link with a phishing URL.” Mark AND/OR. Which single control prunes the most expensive limb?
- 6-char base62 slugs, 100k guesses/sec. About how long to cover the space? What bit length would you pick instead, and how does that change cache key size at 10k create/sec?
Choosing and explaining threat modeling
A complete explanation connects the mechanism to a requirement. Describe what improves when you introduce it, which new failure cases become possible, and how you would know that the trade-off is worthwhile.
Explain the core idea
Assets and actors first, then a DFD with trust boundaries, then STRIDE on each element.
For the crown-jewel goal, grow an attack tree so AND/OR paths show where one control prunes a limb.
End with mitigations, detection, owners, and residual risk—otherwise it is a poster.
When it belongs
Any system-design interview after the happy-path boxes are drawn.
Design reviews for new data stores, admin planes, or public write APIs.
When privacy (LINDDUN) or business-risk processes (PASTA) are in scope.
How it fails
Missing stores and metadata flows from the DFD.
STRIDE as wallpaper with no cheap-path ranking.
Controls without owners or metrics.
Ignoring residual risk, especially phished authentic sessions.
Trade-offs to weigh
Time-boxed journey models versus never-finished estate models.
Prevention cost versus detection coverage.
Privacy modeling extra pass versus missing linkability.
Common misunderstandings
Confusing a pentest report with a threat model.
Treating STRIDE letters as a numeric score.
Modeling only the public API and skipping workers, caches, and admin SSO.
Saying “we use TLS” as the entire tampering story.
Primary references
Short takeaway
Threat modeling is how system design earns its security claims: diagram the flows, walk STRIDE, tree the prize, and write the leftover risk where an interviewer can see it.