Threat Modeling
Purpose
Find the security problems in a design before they are built. Threat modeling is cheap; the same finding after launch is a rewrite, and after a breach it is a disclosure.
When to Use
- Designing a new system or a significant feature.
- Adding a new trust boundary: a new integration, a new user role, a new data flow.
- Before a security review or a compliance audit.
- After an incident, to find the siblings of what just happened.
Capabilities
- Data-flow modeling and trust-boundary identification.
- STRIDE analysis: spoofing, tampering, repudiation, information disclosure, denial of service, elevation of privilege.
- Risk ranking by likelihood and impact.
- Mitigation design and acceptance of residual risk.
Inputs
- The design: components, data flows, and data stores.
- The assets worth protecting and to whom.
- The realistic attacker: an external attacker, a malicious tenant, a compromised dependency, a departing employee.
Outputs
- A data-flow diagram with trust boundaries marked.
- A ranked threat list with mitigations.
- Explicit acceptance of the risks not being mitigated.
Workflow
- Draw the data flow — Components, data stores, external entities, and the flows between them. Simple boxes and arrows; the diagram is a tool, not a deliverable.
- Mark the trust boundaries — Every line data crosses where the trust level changes: internet to your edge, your service to a third party, tenant A's data to tenant B's request. Threats live on these lines.
- Apply STRIDE at each boundary — For each flow, ask each of the six questions. It is mechanical, and that is the point: it finds what intuition skips.
- Rank by realistic risk — Likelihood times impact. A theoretical attack requiring physical access to the datacenter ranks below an IDOR that a bored user could find.
- Design mitigations — For each threat above the acceptance line, a specific control, with an owner.
- Accept the rest explicitly — Write down what you are not mitigating and why. Undocumented acceptance is indistinguishable from oversight.
Best Practices
- Threat model the design, not the code. By the time it is code, the expensive mistakes are already made.
- The most productive question is "what does this component trust, and why?" Trust that has never been examined is where the vulnerabilities are.
- Include the insider and the compromised dependency in the attacker list. Both are more likely than a nation-state, and both are usually ignored.
- A threat with no mitigation and no explicit acceptance is a threat that will be forgotten. There are only two valid states.
- Multi-tenant systems: the highest-value threat is nearly always "tenant A reads tenant B's data". Model it first, and enforce isolation at the lowest layer you can.
- Keep the model updated. A threat model from the original design, two years and forty features later, is a historical document.
Examples
STRIDE applied to one flow, producing a real finding:
Flow: Browser -> API Gateway -> Orders Service -> Postgres
Boundary: internet / internal (at the gateway)
Asset: order data, including customer PII and amounts
S - Spoofing
Threat: Attacker forges a JWT to impersonate another user.
Mitigation: Signature verified with a pinned algorithm (RS256); `alg`
from the header is never trusted. Keys rotated quarterly.
Status: MITIGATED
T - Tampering
Threat: Client modifies `total_cents` in the create-order payload.
Finding: The API currently trusts the client-supplied total.
Mitigation: Server recomputes the total from the line items and the
price list. The client-supplied value is ignored entirely.
Status: ACTION REQUIRED — owner @sam, due 2026-04-02
R - Repudiation
Threat: A user denies having placed an order.
Mitigation: Append-only audit log with the authenticated subject, the
request ID, and the source IP. Retained 7 years.
Status: MITIGATED
I - Information disclosure
Threat: Tenant A reads tenant B's orders by guessing an ID.
Mitigation: ULIDs (not enumerable) AND a row-level security policy on
tenant_id. Two independent controls, because this is the highest-impact
threat in the system.
Status: MITIGATED
D - Denial of service
Threat: An expensive report query is called in a loop.
Mitigation: Rate limit per tenant; query timeout of 5s; the report is
served from a materialized view.
Status: MITIGATED
E - Elevation of privilege
Threat: A read-only API key performs a write.
Finding: Scopes are checked at the gateway but not re-checked in the service.
Risk: A service reachable from inside the VPC bypasses the check entirely.
Mitigation: Enforce scope in the service, not only at the edge.
Status: ACTION REQUIRED — owner @maya, due 2026-04-09
Notes
- The tampering finding above ("the server trusts the client's total") is one of the most common and most costly real-world flaws, and it is found in about five minutes by mechanically walking STRIDE. That is the entire argument for the method.
- Defense in depth means two independent controls for the highest-impact threat. In multi-tenant systems, that is cross-tenant access — application-level filtering and row-level security.
- A threat model produced by one person is a review of that person's blind spots. Do it with at least two people, one of whom did not design the system.
1---2name: threat-modeling3description: Use when designing a system and identifying what could go wrong. Applies STRIDE to a data-flow model, ranks threats by realistic risk, and produces mitigations that are actually built.4---56# Threat Modeling78## Purpose910Find the security problems in a design before they are built. Threat modeling is cheap; the same finding after launch is a rewrite, and after a breach it is a disclosure.1112## When to Use1314- Designing a new system or a significant feature.15- Adding a new trust boundary: a new integration, a new user role, a new data flow.16- Before a security review or a compliance audit.17- After an incident, to find the siblings of what just happened.1819## Capabilities2021- Data-flow modeling and trust-boundary identification.22- STRIDE analysis: spoofing, tampering, repudiation, information disclosure, denial of service, elevation of privilege.23- Risk ranking by likelihood and impact.24- Mitigation design and acceptance of residual risk.2526## Inputs2728- The design: components, data flows, and data stores.29- The assets worth protecting and to whom.30- The realistic attacker: an external attacker, a malicious tenant, a compromised dependency, a departing employee.3132## Outputs3334- A data-flow diagram with trust boundaries marked.35- A ranked threat list with mitigations.36- Explicit acceptance of the risks not being mitigated.3738## Workflow39401. **Draw the data flow** — Components, data stores, external entities, and the flows between them. Simple boxes and arrows; the diagram is a tool, not a deliverable.412. **Mark the trust boundaries** — Every line data crosses where the trust level changes: internet to your edge, your service to a third party, tenant A's data to tenant B's request. Threats live on these lines.423. **Apply STRIDE at each boundary** — For each flow, ask each of the six questions. It is mechanical, and that is the point: it finds what intuition skips.434. **Rank by realistic risk** — Likelihood times impact. A theoretical attack requiring physical access to the datacenter ranks below an IDOR that a bored user could find.445. **Design mitigations** — For each threat above the acceptance line, a specific control, with an owner.456. **Accept the rest explicitly** — Write down what you are not mitigating and why. Undocumented acceptance is indistinguishable from oversight.4647## Best Practices4849- Threat model the design, not the code. By the time it is code, the expensive mistakes are already made.50- The most productive question is "what does this component trust, and why?" Trust that has never been examined is where the vulnerabilities are.51- Include the insider and the compromised dependency in the attacker list. Both are more likely than a nation-state, and both are usually ignored.52- A threat with no mitigation and no explicit acceptance is a threat that will be forgotten. There are only two valid states.53- Multi-tenant systems: the highest-value threat is nearly always "tenant A reads tenant B's data". Model it first, and enforce isolation at the lowest layer you can.54- Keep the model updated. A threat model from the original design, two years and forty features later, is a historical document.5556## Examples5758**STRIDE applied to one flow, producing a real finding:**5960```text61Flow: Browser -> API Gateway -> Orders Service -> Postgres62Boundary: internet / internal (at the gateway)63Asset: order data, including customer PII and amounts6465S - Spoofing66 Threat: Attacker forges a JWT to impersonate another user.67 Mitigation: Signature verified with a pinned algorithm (RS256); `alg`68 from the header is never trusted. Keys rotated quarterly.69 Status: MITIGATED7071T - Tampering72 Threat: Client modifies `total_cents` in the create-order payload.73 Finding: The API currently trusts the client-supplied total.74 Mitigation: Server recomputes the total from the line items and the75 price list. The client-supplied value is ignored entirely.76 Status: ACTION REQUIRED — owner @sam, due 2026-04-027778R - Repudiation79 Threat: A user denies having placed an order.80 Mitigation: Append-only audit log with the authenticated subject, the81 request ID, and the source IP. Retained 7 years.82 Status: MITIGATED8384I - Information disclosure85 Threat: Tenant A reads tenant B's orders by guessing an ID.86 Mitigation: ULIDs (not enumerable) AND a row-level security policy on87 tenant_id. Two independent controls, because this is the highest-impact88 threat in the system.89 Status: MITIGATED9091D - Denial of service92 Threat: An expensive report query is called in a loop.93 Mitigation: Rate limit per tenant; query timeout of 5s; the report is94 served from a materialized view.95 Status: MITIGATED9697E - Elevation of privilege98 Threat: A read-only API key performs a write.99 Finding: Scopes are checked at the gateway but not re-checked in the service.100 Risk: A service reachable from inside the VPC bypasses the check entirely.101 Mitigation: Enforce scope in the service, not only at the edge.102 Status: ACTION REQUIRED — owner @maya, due 2026-04-09103```104105## Notes106107- The tampering finding above ("the server trusts the client's total") is one of the most common and most costly real-world flaws, and it is found in about five minutes by mechanically walking STRIDE. That is the entire argument for the method.108- Defense in depth means two independent controls for the highest-impact threat. In multi-tenant systems, that is cross-tenant access — application-level filtering *and* row-level security.109- A threat model produced by one person is a review of that person's blind spots. Do it with at least two people, one of whom did not design the system.