Attack Trees vs STRIDE for Feature Design

Two techniques dominate practical threat analysis, and teams often treat them as rivals when they answer different questions. Category enumeration — the STRIDE framework approach — asks “what could go wrong with each component”, which is a breadth question. An attack tree asks “how could this specific bad outcome be reached”, which is a depth question. Choosing badly wastes the analysis budget: enumerating categories across forty components when you care about one outcome, or building a beautiful tree while three unexamined components sit outside it.

This guide compares them honestly, shows how they compose, and — most importantly — ends both in acceptance criteria rather than in a diagram. It is part of the Security Requirements & Abuse Cases guide within Threat Modeling Fundamentals & Methodology.

Prerequisites

  • A design worth analysing: a new feature, a new integration, or a significant change to an existing flow
  • A component or data-flow view of the design, however rough
  • Someone able to say which outcomes would be genuinely serious for the business
  • The acceptance-criteria practice, so the output has somewhere to go

Expected Outcomes

  • A deliberate choice of technique per situation rather than a habit
  • A broad pass that shows where risk concentrates
  • A tree for the one or two outcomes that must not happen
  • Every reachable leaf converted into a testable criterion

Step 1: Understand What Each Technique Is For

Dimension Category enumeration Attack tree
Question answered What could go wrong here? How could this specific outcome be reached?
Organised by Component and data flow A single attacker goal
Output shape A matrix of components against categories A tree of paths with leaves
Strength Systematic coverage; hard to skip a component Finds multi-step paths and non-technical routes
Weakness Misses chains that cross components Says nothing about components outside the tree
Cost Low, and teachable in an afternoon Higher, and needs an experienced facilitator
Best used Every design, as the default pass The one or two outcomes with severe consequences
Breadth Across Components, Depth Toward One Goal On the left, enumeration walks every component against every category, producing systematic coverage where each cell is a small question. On the right, a tree starts from one serious outcome and decomposes the alternative routes that reach it, including routes that cross several components or leave the technical system entirely. Enumeration — breadth component spoof tamper disclose gateway auth payouts every cell is one small question, and no component gets skipped Attack tree — depth goal: payout redirected take over the account abuse support flow reset via mailbox social engineer an agent paths cross components and sometimes leave the system entirely

Step 2: Run the Broad Pass First

Enumeration is cheap and tells you where to spend the expensive technique. Work through the components in the design and record one line per category that produces a real question.

## Broad pass — payout account change feature
| Component        | Category            | Question that has a real answer                     |
|------------------|---------------------|------------------------------------------------------|
| Payout API       | Spoofing            | Can the request be made without a recent factor?     |
| Payout API       | Tampering           | Can the destination be changed after approval?       |
| Payout API       | Repudiation         | Does the audit entry name who approved which target? |
| Notification job | Info disclosure     | Does the confirmation mail show the full account?    |
| Support console  | Elevation           | Can an agent change a payout account unilaterally?   |
| Payout API       | Denial of service   | Can changes be spammed to bury the real alert?       |

Six lines, and the shape of the risk is already visible: three of them concentrate on one component and one is entirely outside the technical system. That concentration is the signal to go deeper on one goal.


Step 3: Build a Tree for the Outcome That Must Not Happen

## Goal: an attacker redirects a payout to an account they control

1. Take over the account
   1.1 Guess or stuff the password           → mitigated: throttling + breach screening
   1.2 Phish the password and a code         → mitigated: origin-bound factor
   1.3 Steal a live session                  → partially: short idle timeout; step-up on this operation
   1.4 Reset the password via the mailbox     → GAP: reset does not require the enrolled factor
2. Change the destination without account takeover
   2.1 Modify the request after approval      → mitigated: challenge bound to the destination
   2.2 Race two concurrent changes            → GAP: last write wins, no re-challenge
3. Use a human path
   3.1 Persuade a support agent               → GAP: agents can change payout details unilaterally
   3.2 Compromise an agent's own account      → partially: agents use factors; no step-up on this action
4. Reach the database directly
   4.1 Injection in an admin report            → mitigated: parameter binding, tested
   4.2 Compromise the cloud provider           → ACCEPTED ASSUMPTION, documented

Three gaps and one accepted assumption. Note where they are: one in a flow the feature does not own (password reset), one in a concurrency edge nobody had considered, and one entirely in the support process. A component-by-component pass would very likely have found none of the three, because none of them lives in the payout component.

None of the Three Gaps Lives in the Component Being Built The first gap sits in the password reset flow, which the payout feature does not own. The second is a concurrency edge nobody had considered. The third is entirely in the support process and involves no code at all. A component-by-component enumeration would very likely have found none of them. Gap 1 — in a flow this feature does not own the reset path bypasses the factor, so the payout control is reachable around it Gap 2 — a concurrency edge nobody considered two changes race, last write wins, and the approved destination is not the applied one Gap 3 — entirely in the support process an agent can change payout details unilaterally; no code is involved, and no scanner sees it

Step 4: Convert Leaves Into Criteria

## From the tree, into tickets
- Leaf 1.4 → TICKET-4201  "Password reset requires the enrolled factor where one exists"
  AC: with a factor enrolled, completing a reset without answering it returns 401.
- Leaf 2.2 → TICKET-4202  "Payout change re-challenges when the destination differs from the approved one"
  AC: two concurrent changes with different destinations result in one applied and one 409.
- Leaf 3.1 → TICKET-4203  "Support-initiated payout changes require owner confirmation"
  AC: an agent-initiated change enters pending state and applies only after the owner confirms.
- Leaf 4.2 → RISK-2026-016  accepted assumption, owner named, review annually.

This step is what separates a technique from a ritual. A tree that ends as a diagram in a wiki has cost a day and changed nothing; a tree that ends as three tickets and one recorded assumption has done its job — and the tickets are the artefact that will still exist when the diagram has been forgotten.

Use Both, in This Order The broad pass runs first and is cheap, showing where risk concentrates. A tree is then built for the one or two outcomes that would be most serious, which finds multi-step and non-technical paths. Every reachable leaf becomes a ticket with acceptance criteria, and every unreachable one becomes a recorded assumption. 1 · Broad pass cheap, systematic, shows concentration 2 · Tree, one goal multi-step and human paths appear here 3 · Tickets and assumptions every reachable leaf gets a criterion; the rest are recorded, with owners Skipping stage 1 leaves components unexamined; skipping stage 3 leaves a diagram that changes nothing. Stage 2 is worth its cost for one or two goals per design — rarely more.

Verification

The analysis worked if it produced work that would not otherwise have existed:

# 1. Tickets traceable to a specific leaf or matrix cell.
grep -RIl 'threat-source:' .tickets/ | xargs grep -h 'threat-source:' | sort | uniq -c

# 2. Accepted assumptions recorded with an owner and a review date.
python3 tools/check_accepted_risks.py risks/accepted/

# 3. At least one gap found by the tree that the broad pass missed.
#    (If the tree never finds anything new, stop building trees — the signal is honest.)

That third line is deliberate. Techniques should earn their place, and a team that runs trees for four consecutive designs without finding a path the matrix missed has learned something useful about where to spend its time.


Troubleshooting

Symptom Likely cause Fix
Tree grows to hundreds of leaves No stopping rule Stop at any leaf that is mitigable or acceptable; record assumptions explicitly
Enumeration produces the same generic threats every time Applied to the system rather than to this design’s components Enumerate against the specific components and flows the design introduces
Neither technique produces tickets The analysis ends at the diagram Require a leaf-to-ticket mapping before the session is considered complete
Sessions take a full day Both techniques applied to everything Broad pass on everything, tree on one or two goals only
Non-technical paths never appear Only the technical system considered Include support flows, offboarding and business processes as branches
The output is never revisited Not linked from the design document or the tickets Reference the analysis by identifier from both, and review it when the design changes

Common Implementation Mistakes


Frequently Asked Questions

Can a team use only one of the two?

Most teams should default to category enumeration: it is systematic, teachable in an afternoon, cheap to repeat, and its output maps directly onto components. Attack trees earn their extra cost on a small number of outcomes that would be genuinely serious — a payout redirected, an account fully taken over, a tenant boundary crossed. Using only trees tends to leave whole components unexamined; using only enumeration tends to miss the multi-step path that chains three individually minor issues into one severe one.

How deep should an attack tree go?

Until every leaf is either something you can mitigate or something you consciously accept. When a leaf reads “the attacker compromises the cloud provider”, stop and record it as an accepted assumption with an owner. Depth without that stopping rule produces trees that look impressive and cannot be acted on, and that is the usual reason a team tries the technique once and never returns to it.

Do attack trees belong in the ticket like abuse cases do?

The tree belongs in the design document; its leaves belong in the tickets. A tree is a design-time artefact spanning several stories, and pasting it into one ticket makes it noise that people learn to scroll past. Reference it by identifier from the tickets it produced, and convert each reachable leaf into an acceptance criterion attached to whichever story owns that path.