Threat Modeling Methodology
Why This Changes Everything
Code review finds bugs in code. Threat modeling finds bugs in design. A vulnerability caught at design time costs 10 minutes to fix. The same vulnerability caught in production costs days and potentially millions.
The 0.01% mindset: Before any code is written, the architecture is reviewed against every attack category. The code reviewer's job becomes verification ("did they implement the mitigations we designed?") instead of discovery ("is there a vulnerability hiding somewhere?").
When to Threat Model
- New product/feature: Before Maks writes a single line
- Architecture change: New API, new data flow, new third-party integration
- Security-sensitive feature: Auth, payments, admin, data export, AI inference
- External attack surface change: New domain, new API endpoint, new WebSocket
The Process (4 Steps)
Step 1: Decompose the System
Draw the Data Flow Diagram (DFD)
Map every:
- External entity: Users, third-party APIs, payment providers
- Process: Next.js server, API routes, Server Actions, Edge Functions, cron jobs
- Data store: Supabase DB, Redis cache, Supabase Storage, file system
- Data flow: HTTP requests, WebSocket messages, database queries, API calls
- Trust boundary: Client ↔ Server, Server ↔ Database, Server ↔ Third-party
┌─────────────────────────────────────────────────────────────┐
│ TRUST BOUNDARY: Internet │
│ │
│ [Browser] ←──HTTP/WS──→ [Next.js Server] │
│ [Mobile App] │ │
│ │ │
│ ┌───────────────────┼──────────────────┐ │
│ │ TRUST BOUNDARY: Internal │ │
│ │ │ │ │
│ │ [Supabase DB] ←──SQL──→ [Server] │ │
│ │ [Redis Cache] ←──Redis──→ [Server] │ │
│ │ [Storage] ←──S3──→ [Server] │ │
│ │ │ │
│ └───────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────┼──────────────────┐ │
│ │ TRUST BOUNDARY: Third-party │ │
│ │ │ │
│ │ [Stripe API] ←──HTTPS──→ [Server] │ │
│ │ [Claude API] ←──HTTPS──→ [Server] │ │
│ │ [GitHub API] ←──HTTPS──→ [Server] │ │
│ └───────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Identify Trust Boundaries
Every arrow that crosses a trust boundary is an attack surface. Each must be analyzed:
- What data crosses this boundary?
- Is it authenticated?
- Is it encrypted?
- Is input validated?
- Can it be intercepted or forged?
Step 2: Apply STRIDE to Every Component
STRIDE categorizes threats. For each component and data flow in the DFD, systematically ask:
| Threat |
Question |
Example in Our Stack |
| Spoofing |
Can an attacker pretend to be someone else? |
Forge JWT, bypass auth, impersonate user via IDOR |
| Tampering |
Can an attacker modify data in transit or at rest? |
Modify request body, tamper with cached data, SQL injection |
| Repudiation |
Can an actor deny performing an action? |
Delete logs, no audit trail on admin actions |
| Information Disclosure |
Can an attacker access data they shouldn't? |
Cross-tenant data leak, verbose errors, missing RLS |
| Denial of Service |
Can an attacker make the system unavailable? |
ReDoS, resource exhaustion, cost attack on AI endpoints |
| Elevation of Privilege |
Can an attacker gain unauthorized capabilities? |
Become admin, access service_role, bypass feature gates |
STRIDE Applied to Common Components
API Route (POST /api/challenges):
| Threat |
Applicable? |
Mitigation |
| Spoofing |
Yes — can unauthenticated user call it? |
Require auth (getUser()) |
| Tampering |
Yes — can input be manipulated? |
Zod validation, parameterized queries |
| Repudiation |
Maybe — are challenge creations logged? |
Audit log with user_id and timestamp |
| Info Disclosure |
Yes — do errors leak schema? |
Generic error responses |
| DoS |
Yes — can it be called unlimited times? |
Rate limiting |
| EoP |
Yes — can regular user create admin-only challenges? |
Role check before creation |
Supabase Database Table:
| Threat |
Applicable? |
Mitigation |
| Spoofing |
N/A at DB level |
Auth handled at API layer |
| Tampering |
Yes — can wrong user modify rows? |
RLS with USING + WITH CHECK |
| Info Disclosure |
Yes — can wrong user read rows? |
RLS SELECT policies |
| EoP |
Yes — can user modify their own role? |
RLS prevents user_role column updates |
Step 3: Build Attack Trees for Critical Paths
For the most critical assets (user accounts, payment data, admin access), build attack trees:
GOAL: Steal User's Payment Information
├── Via Database Access
│ ├── Missing RLS on payment_methods table
│ ├── SQL injection in search endpoint
│ └── Service role key leaked to frontend
├── Via API Response
│ ├── IDOR on /api/payment-methods?userId=X
│ ├── Verbose error includes payment data
│ └── Cross-tenant data leak in multi-tenant query
├── Via Client-Side
│ ├── XSS steals session → accesses payment API
│ ├── Payment data stored in localStorage
│ └── Payment form on HTTP (not HTTPS)
├── Via Third-Party
│ ├── Stripe webhook forgery → manipulate payment records
│ ├── Compromised dependency reads payment data
│ └── SSRF → internal API → payment service
└── Via Physical/Social
├── Session token in URL → shared/logged
├── Admin password brute force
└── Social engineering support staff
Each leaf is a potential attack. Each requires a specific mitigation.
Step 4: Create Threat-Mitigation Map
For each identified threat, specify the mitigation:
| ID |
Threat |
Component |
STRIDE |
Severity |
Mitigation |
Status |
| T1 |
Unauthenticated API access |
/api/challenges |
S |
HIGH |
getUser() check |
Required |
| T2 |
Input injection |
/api/challenges |
T |
HIGH |
Zod schema validation |
Required |
| T3 |
Cross-tenant data leak |
challenges table |
I |
CRITICAL |
RLS with org_id scoping |
Required |
| T4 |
Admin bypass |
/api/admin/* |
E |
CRITICAL |
Role verification middleware |
Required |
| T5 |
Brute force login |
/api/auth/login |
S,D |
HIGH |
Rate limiting + CAPTCHA |
Required |
This becomes the security requirements document for the feature.
Threat Model Template
# Threat Model: [Feature Name]
Date: [Date]
Author: Forge
Status: [Draft/Review/Approved]
## 1. System Description
[1-2 paragraphs describing what the feature does]
## 2. Data Flow Diagram
[DFD with trust boundaries marked]
## 3. Assets
| Asset | Sensitivity | Storage | Access Control |
|-------|------------|---------|---------------|
| [e.g., User credentials] | CRITICAL | Supabase auth | Auth service only |
## 4. Trust Boundaries
| Boundary | Data Crossing | Protection |
|----------|--------------|------------|
| Client ↔ Server | Auth tokens, user input | HTTPS, JWT verification |
## 5. STRIDE Analysis
[Table per component as shown above]
## 6. Attack Trees
[For top 3 critical assets]
## 7. Threat-Mitigation Map
[Full table with mitigations and status]
## 8. Residual Risks
[Threats that can't be fully mitigated — accepted with justification]
## 9. Review Verification Points
[What to check during code review to verify mitigations are implemented]
Integration with Forge Workflow
Before Maks Builds (Architecture Phase)
- Forge receives architecture spec
- Forge creates threat model for the feature
- Threat model reviewed with Nick/MaksPM
- Mitigations become security requirements in the spec
- Maks implements with requirements built in
During Code Review (Review Phase)
- Load the threat model for the feature being reviewed
- For each mitigation in the threat map: verify it's implemented
- Findings reference specific threat IDs: "T3 not mitigated — missing RLS on new table"
This closes the loop: design-time threat identification → implementation-time verification.
References
For STRIDE details and examples, see references/stride-reference.md.
For attack tree construction patterns, see references/attack-trees.md.
1---2name: threat-modeling-methodology3description: Systematic threat modeling using STRIDE, attack trees, and data flow analysis — applied BEFORE code is written to catch security flaws at design time. Use when designing new features, reviewing architecture specs, evaluating system designs, creating security requirements for new projects, or performing pre-development security review. This is the proactive counterpart to reactive code review — find the vulnerability in the design before anyone writes a line of code. Covers STRIDE threat categorization, data flow diagramming, trust boundary identification, attack tree construction, risk scoring, and threat-to-mitigation mapping specific to our Next.js + Supabase + Vercel stack.4---56# Threat Modeling Methodology78## Why This Changes Everything910Code review finds bugs in code. Threat modeling finds bugs in **design**. A vulnerability caught at design time costs 10 minutes to fix. The same vulnerability caught in production costs days and potentially millions.1112**The 0.01% mindset**: Before any code is written, the architecture is reviewed against every attack category. The code reviewer's job becomes verification ("did they implement the mitigations we designed?") instead of discovery ("is there a vulnerability hiding somewhere?").1314## When to Threat Model1516- **New product/feature**: Before Maks writes a single line17- **Architecture change**: New API, new data flow, new third-party integration18- **Security-sensitive feature**: Auth, payments, admin, data export, AI inference19- **External attack surface change**: New domain, new API endpoint, new WebSocket2021## The Process (4 Steps)2223### Step 1: Decompose the System2425#### Draw the Data Flow Diagram (DFD)26Map every:27- **External entity**: Users, third-party APIs, payment providers28- **Process**: Next.js server, API routes, Server Actions, Edge Functions, cron jobs29- **Data store**: Supabase DB, Redis cache, Supabase Storage, file system30- **Data flow**: HTTP requests, WebSocket messages, database queries, API calls31- **Trust boundary**: Client ↔ Server, Server ↔ Database, Server ↔ Third-party3233```34┌─────────────────────────────────────────────────────────────┐35│ TRUST BOUNDARY: Internet │36│ │37│ [Browser] ←──HTTP/WS──→ [Next.js Server] │38│ [Mobile App] │ │39│ │ │40│ ┌───────────────────┼──────────────────┐ │41│ │ TRUST BOUNDARY: Internal │ │42│ │ │ │ │43│ │ [Supabase DB] ←──SQL──→ [Server] │ │44│ │ [Redis Cache] ←──Redis──→ [Server] │ │45│ │ [Storage] ←──S3──→ [Server] │ │46│ │ │ │47│ └───────────────────────────────────────┘ │48│ │ │49│ ┌───────────────────┼──────────────────┐ │50│ │ TRUST BOUNDARY: Third-party │ │51│ │ │ │52│ │ [Stripe API] ←──HTTPS──→ [Server] │ │53│ │ [Claude API] ←──HTTPS──→ [Server] │ │54│ │ [GitHub API] ←──HTTPS──→ [Server] │ │55│ └───────────────────────────────────────┘ │56└─────────────────────────────────────────────────────────────┘57```5859#### Identify Trust Boundaries60Every arrow that crosses a trust boundary is an **attack surface**. Each must be analyzed:61- What data crosses this boundary?62- Is it authenticated?63- Is it encrypted?64- Is input validated?65- Can it be intercepted or forged?6667### Step 2: Apply STRIDE to Every Component6869STRIDE categorizes threats. For each component and data flow in the DFD, systematically ask:7071| Threat | Question | Example in Our Stack |72|--------|----------|---------------------|73| **S**poofing | Can an attacker pretend to be someone else? | Forge JWT, bypass auth, impersonate user via IDOR |74| **T**ampering | Can an attacker modify data in transit or at rest? | Modify request body, tamper with cached data, SQL injection |75| **R**epudiation | Can an actor deny performing an action? | Delete logs, no audit trail on admin actions |76| **I**nformation Disclosure | Can an attacker access data they shouldn't? | Cross-tenant data leak, verbose errors, missing RLS |77| **D**enial of Service | Can an attacker make the system unavailable? | ReDoS, resource exhaustion, cost attack on AI endpoints |78| **E**levation of Privilege | Can an attacker gain unauthorized capabilities? | Become admin, access service_role, bypass feature gates |7980#### STRIDE Applied to Common Components8182**API Route (POST /api/challenges)**:83| Threat | Applicable? | Mitigation |84|--------|------------|------------|85| Spoofing | Yes — can unauthenticated user call it? | Require auth (getUser()) |86| Tampering | Yes — can input be manipulated? | Zod validation, parameterized queries |87| Repudiation | Maybe — are challenge creations logged? | Audit log with user_id and timestamp |88| Info Disclosure | Yes — do errors leak schema? | Generic error responses |89| DoS | Yes — can it be called unlimited times? | Rate limiting |90| EoP | Yes — can regular user create admin-only challenges? | Role check before creation |9192**Supabase Database Table**:93| Threat | Applicable? | Mitigation |94|--------|------------|------------|95| Spoofing | N/A at DB level | Auth handled at API layer |96| Tampering | Yes — can wrong user modify rows? | RLS with USING + WITH CHECK |97| Info Disclosure | Yes — can wrong user read rows? | RLS SELECT policies |98| EoP | Yes — can user modify their own role? | RLS prevents user_role column updates |99100### Step 3: Build Attack Trees for Critical Paths101102For the most critical assets (user accounts, payment data, admin access), build attack trees:103104```105GOAL: Steal User's Payment Information106├── Via Database Access107│ ├── Missing RLS on payment_methods table108│ ├── SQL injection in search endpoint109│ └── Service role key leaked to frontend110├── Via API Response111│ ├── IDOR on /api/payment-methods?userId=X112│ ├── Verbose error includes payment data113│ └── Cross-tenant data leak in multi-tenant query114├── Via Client-Side115│ ├── XSS steals session → accesses payment API116│ ├── Payment data stored in localStorage117│ └── Payment form on HTTP (not HTTPS)118├── Via Third-Party119│ ├── Stripe webhook forgery → manipulate payment records120│ ├── Compromised dependency reads payment data121│ └── SSRF → internal API → payment service122└── Via Physical/Social123 ├── Session token in URL → shared/logged124 ├── Admin password brute force125 └── Social engineering support staff126```127128Each leaf is a potential attack. Each requires a specific mitigation.129130### Step 4: Create Threat-Mitigation Map131132For each identified threat, specify the mitigation:133134| ID | Threat | Component | STRIDE | Severity | Mitigation | Status |135|----|--------|-----------|--------|----------|------------|--------|136| T1 | Unauthenticated API access | /api/challenges | S | HIGH | getUser() check | Required |137| T2 | Input injection | /api/challenges | T | HIGH | Zod schema validation | Required |138| T3 | Cross-tenant data leak | challenges table | I | CRITICAL | RLS with org_id scoping | Required |139| T4 | Admin bypass | /api/admin/* | E | CRITICAL | Role verification middleware | Required |140| T5 | Brute force login | /api/auth/login | S,D | HIGH | Rate limiting + CAPTCHA | Required |141142This becomes the **security requirements document** for the feature.143144## Threat Model Template145146```markdown147# Threat Model: [Feature Name]148Date: [Date]149Author: Forge150Status: [Draft/Review/Approved]151152## 1. System Description153[1-2 paragraphs describing what the feature does]154155## 2. Data Flow Diagram156[DFD with trust boundaries marked]157158## 3. Assets159| Asset | Sensitivity | Storage | Access Control |160|-------|------------|---------|---------------|161| [e.g., User credentials] | CRITICAL | Supabase auth | Auth service only |162163## 4. Trust Boundaries164| Boundary | Data Crossing | Protection |165|----------|--------------|------------|166| Client ↔ Server | Auth tokens, user input | HTTPS, JWT verification |167168## 5. STRIDE Analysis169[Table per component as shown above]170171## 6. Attack Trees172[For top 3 critical assets]173174## 7. Threat-Mitigation Map175[Full table with mitigations and status]176177## 8. Residual Risks178[Threats that can't be fully mitigated — accepted with justification]179180## 9. Review Verification Points181[What to check during code review to verify mitigations are implemented]182```183184## Integration with Forge Workflow185186### Before Maks Builds (Architecture Phase)1871. Forge receives architecture spec1882. Forge creates threat model for the feature1893. Threat model reviewed with Nick/MaksPM1904. Mitigations become security requirements in the spec1915. Maks implements with requirements built in192193### During Code Review (Review Phase)1941. Load the threat model for the feature being reviewed1952. For each mitigation in the threat map: verify it's implemented1963. Findings reference specific threat IDs: "T3 not mitigated — missing RLS on new table"197198This closes the loop: **design-time threat identification → implementation-time verification**.199200## References201202For STRIDE details and examples, see `references/stride-reference.md`.203For attack tree construction patterns, see `references/attack-trees.md`.