Skip to main content
Security

Threat Modeling That Ships Before the Feature

Ravinder··12 min read
SecurityThreat ModelingSTRIDEEngineering
Share:
Threat Modeling That Ships Before the Feature

Security Reviews That Happen After the Breach

The standard security review process at most companies goes like this: engineers build a feature, ship it to staging, and then either (a) the security team reviews it weeks later and files a Jira ticket that becomes backlog debt, or (b) nobody reviews it and the first "security review" is a CVE disclosure.

Neither outcome is acceptable, and both share the same root cause: security analysis is treated as a gate at the end of the process rather than an input at the beginning.

Threat modeling is the discipline of thinking adversarially about a system before it's built. The problem is that every published methodology — STRIDE, PASTA, LINDDUN — comes with enough process overhead to fill a two-day workshop. That's fine for a compliance exercise. It's death for a team trying to ship weekly.

This post is about running a threat model that fits in a sprint. The output is a prioritized list of risks the team owns, not a PDF that goes into a SharePoint folder nobody reads.

What to Threaten-Model and What to Skip

The most important skill in agile threat modeling is scope control. You cannot threat-model every ticket. You should threat-model features that:

  • Introduce new trust boundaries (a new API endpoint, a new external integration, a new user role)
  • Handle sensitive data for the first time (PII, payment info, health records)
  • Change authentication or authorization logic
  • Add new infrastructure (a new service, a new database, a new queue)

You can skip threat-modeling for:

  • UI copy changes
  • Performance optimizations with no data flow changes
  • Internal tooling with no external attack surface
  • Bug fixes that don't touch auth or data handling

A simple heuristic: if the PR description would make a security engineer say "wait, what's the threat here?" — model it. If it's a CSS change, don't.

The STRIDE Framework in 45 Minutes

STRIDE is a mnemonic for six threat categories: Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege. It was designed for whiteboard sessions, which means it's actually well-suited to sprint planning — if you apply it narrowly.

The format that works in practice is a 45-minute session with four participants: the engineer who owns the feature, a second engineer for challenge, the product manager for context, and optionally a security engineer if one is available. The output is a filled STRIDE table and a list of mitigations that go into the sprint or the next one.

flowchart LR A[Feature ticket\nin sprint planning] -->|trust boundary?| B{Scope check} B -->|Yes| C[45-min STRIDE session] B -->|No| D[Skip — no model needed] C --> E[Draw data flow\n5 minutes] E --> F[Walk STRIDE\ncategories\n25 minutes] F --> G[Assign mitigations\nto tickets\n10 minutes] G --> H[Document owner\nand sprint target] H --> I[Mitigation tickets\nin backlog]

The Data Flow Sketch

Before you can apply STRIDE, you need a quick data flow diagram (DFD). This does not need to be formal. On a whiteboard or in a Miro board, draw:

  1. External actors (users, third-party services, other systems)
  2. Your service boundaries
  3. Data stores (databases, caches, queues)
  4. The data that flows between them

For a new "user uploads a profile photo" feature, the DFD might be:

[User] -- HTTP multipart POST --> [Upload API] -- validated file --> [S3 bucket]
[Upload API] -- write metadata --> [Postgres]
[User] -- GET signed URL --> [Upload API] -- generate --> [CloudFront CDN]

That's enough. You're not writing a formal spec — you're giving the STRIDE walk something concrete to anchor against.

Walking the STRIDE Categories

For each component and data flow in the sketch, ask the corresponding STRIDE question:

Category Question Example for the upload feature
Spoofing Can an attacker pretend to be someone else? Can a user upload a photo as another user's profile?
Tampering Can data be modified in transit or at rest? Can a malicious file bypass content validation?
Repudiation Can actions be denied or untraceable? Is the uploader logged with immutable audit trail?
Info Disclosure Can sensitive data be exposed? Can a user enumerate other users' photo URLs?
Denial of Service Can the feature be abused to exhaust resources? Can someone upload 10GB files in a loop?
Elevation of Privilege Can a user gain capabilities above their role? Can a free-tier user upload to a premium storage path?

Not every category will yield a real threat for every feature. That's expected. The value is in the systematic walk — it surfaces the threat that "obviously" wasn't a problem until someone asked the right question.

Attack Trees for High-Priority Threats

When STRIDE surfaces a threat that looks serious, an attack tree helps you reason about the realistic exploitation paths without getting lost in hypotheticals.

An attack tree starts with the attacker's goal at the root and decomposes it into the sub-goals they'd need to achieve. The branches that matter are the ones that are both technically feasible and economically rational for an attacker.

For the "access another user's uploaded photo" threat:

graph TD A[Goal: Access another\nuser's private photo] A --> B[Predict or enumerate\nsigned URL] A --> C[Exploit broken\naccess control on API] A --> D[Compromise CloudFront\nor S3 config] B --> B1[S3 key not random\nenough - LOW effort] B --> B2[URL exposed in\nresponse body - LOW effort] C --> C1[Missing auth check\non GET /photos/:id] C --> C2[IDOR via sequential\nuser ID in URL] D --> D1[Bucket ACL\nmisconfigured - requires\nAWS-level access]

The attack tree immediately shows that B1, B2, C1, and C2 are the realistic paths — they require no special access and are basic web vulnerabilities. D1 requires AWS account access, which is a different threat class entirely. The team can now prioritize: ensure S3 keys use 128 bits of randomness, add an auth check on every photo retrieval endpoint, and use UUIDs not sequential IDs.

This analysis takes 10 minutes for a well-understood threat. It's enough.

What to Actually Skip

A complete threat model can run for days. In a sprint context, skip:

Physical attacks. Unless you operate physical infrastructure, attacks requiring physical access to hardware are out of scope for a feature-level threat model.

Nation-state adversaries. If your threat actor requires the resources of a government intelligence agency, you're not going to mitigate it in a sprint. Document it and move on.

Third-party service internals. You can model how you interact with Stripe, but you can't model Stripe's internal security. Assume the SaaS vendors you use are not compromised and focus on your integration layer.

Theoretical zero-days. "An attacker could find a zero-day in OpenSSL" is not a useful threat model entry. You can't fix OpenSSL in a sprint.

The exhaustive combinatorial space. STRIDE applied to every possible combination of components in a large system produces hundreds of entries. Apply it to the new or changed components only — the rest has already shipped and has its own risk profile.

Embedding Threat Models in PR Review

The threat model output — a list of threats and their assigned mitigations — should live somewhere engineers encounter it during code review, not in a separate security backlog nobody checks.

The minimal implementation is a THREAT_MODEL.md in the PR or a structured comment template:

## Threat Model Summary
 
**Feature:** User profile photo upload
**Session date:** 2026-04-07
**Owner:** @eng-alice
 
### Identified Threats
 
| ID | Category | Description | Mitigation | Status |
|----|----------|-------------|------------|--------|
| T1 | Tampering | Malicious file bypass via polyglot | Server-side magic byte validation + re-encode images | In this PR |
| T2 | Info Disclosure | Photo URL enumeration via weak S3 key | Use 128-bit random key in S3 object path | In this PR |
| T3 | DoS | Unbounded upload size | Enforce 10MB limit at API gateway and application layer | In this PR |
| T4 | Elevation of Privilege | Free-tier user accesses premium storage path | Add tier check in upload handler + integration test | Next sprint |
 
### Out of Scope
- S3 bucket ACL misconfiguration (covered by infrastructure policy baseline)
- Physical server access

This template in a PR description means reviewers can see the threat model as they review the code. They can verify that T1 is actually addressed by looking at the validation logic. They can flag if T4 wasn't picked up. The threat model becomes part of the review, not a separate artifact.

Assigning Ownership

Threat models that produce a list of findings with no owner get treated exactly like unassigned Jira tickets: ignored until something breaks.

The rule is simple: every threat gets an owner who is accountable for the mitigation, and every mitigation gets a sprint target. The owner doesn't have to build the fix themselves, but they're responsible for ensuring it ships.

For high-impact threats (information disclosure of PII, elevation of privilege in auth flows), the mitigation should be in the same sprint as the feature. Shipping the feature without the mitigation is not acceptable. For medium threats, the next sprint is fine. For low threats, add to the backlog with a label and revisit quarterly.

The accountability model only works if the team treats unmitigated high-impact threats as release blockers. This is a cultural decision that engineering leadership has to make explicit.

Tracking Threat Model Coverage Over Time

Running a threat model for one feature is useful. Running them consistently and tracking the results over time is where the compounding value comes from.

A simple coverage metric: of the features that touched trust boundaries this quarter, what percentage had a completed threat model before shipping? This number tells you whether the practice is actually embedded or whether it happens only when someone remembers to schedule the session.

Track this in the same place you track other engineering health metrics — a dashboard or a weekly engineering summary. If coverage drops below 80%, it's a signal that the process has too much friction. The fix is usually one of: the session format is too long (cut it), the trigger criteria are unclear (sharpen them), or there's no one holding the team accountable (assign a rotation).

Quarterly, do a brief retrospective on the threats that were identified and the mitigations that were built. Ask two questions:

  1. Of the threats we identified, how many mitigations were actually completed? If this number is low, the ownership model is broken.
  2. Were there any real security issues this quarter that a threat model would have caught? If yes, analyze why the threat model didn't happen or didn't surface that issue.

This retrospective loop is what differentiates a threat modeling practice from a threat modeling exercise.

The Cultural Shift

The hardest part of embedding threat modeling in a sprint is not the methodology — STRIDE is simple enough to teach in an hour. The hard part is changing the team's default assumption from "security is someone else's job" to "we are responsible for thinking adversarially about what we build."

This shift happens faster when threat models catch real issues. When the first session uncovers a broken access control that would have been an embarrassing incident if it shipped, the team understands concretely why the 45 minutes was worth it. Until that happens, it can feel like process theater.

The practical implication: in the first few sessions, pick features where you already suspect a security gap. Stack the deck for an early win. The goal is to demonstrate value fast enough that the team internalizes the habit before anyone decides the overhead isn't worth it.

Integrating with Automated Security Tooling

Threat modeling is human-driven analysis — it catches design-level issues that scanners miss entirely. But it works best alongside automated tooling that catches implementation-level issues the human session didn't focus on.

The complementary tools:

SAST (Static Application Security Testing) — runs in CI, catches common implementation vulnerabilities like SQL injection, XSS, and hardcoded secrets. This catches the execution of a threat that the threat model identified but a developer inadvertently introduced anyway.

DAST (Dynamic Application Security Testing) — runs against a running service, tests OWASP Top 10 patterns by actually sending malicious payloads. OWASP ZAP and Burp Suite both have CI-integrable modes.

Dependency scanning — flags known CVEs in your dependency tree. Separate from threat modeling, but addresses a category of threats (supply chain) that threat models rarely model well.

The mental model is layers: the threat model identifies what categories of risk to care about for this feature; SAST checks if the implementation accidentally introduced those risks; DAST confirms the running service doesn't expose them; dependency scanning watches for third-party introductions.

None of these replace threat modeling. A scanner cannot tell you that your authorization model has a logical flaw that lets a free-tier user access premium data — that requires a human thinking adversarially about the design. But scanners catch the implementation errors that humans miss when they're moving fast, and they run on every commit without scheduling overhead.

Key Takeaways

  • Threat modeling fits in a sprint if you scope it to trust boundaries and sensitive data changes — not every ticket needs a model.
  • A 45-minute STRIDE session with four people produces more actionable output than a two-day formal workshop for most feature-level threats.
  • Attack trees are the right tool for drilling into a specific high-priority threat — they surface realistic exploitation paths and help you deprioritize theoretical-but-implausible ones.
  • Skip physical attacks, nation-state adversaries, and third-party service internals — they're out of scope for feature-level threat modeling and distract from the real risks.
  • Embedding the threat model output in the PR template means reviewers can verify mitigations during code review, closing the loop between analysis and implementation.
  • Every threat needs an owner and a sprint target — threat models with no assigned accountability produce backlog debt, not security improvement.