Threat modeling with STRIDE – a worked example.
We pick a tiny web app and walk it through STRIDE, then write the prompt that gets an LLM to do 60% of the same work as a reviewer.
Threat modeling sounds heavy. In practice the useful version of it is a one-hour exercise with a whiteboard and the STRIDE acronym. We will do it for a deliberately small app, then show how to extract the same work from an LLM at scale.
The system: a paste service
Users paste text, get back a URL with a random ID, share the URL. Pastes optionally expire. There are no accounts. The architecture is four boxes:
[ Browser ] →https→ [ Web ] →TCP→ [ DB ]
↓
[ Cache ]
STRIDE in one paragraph each
STRIDE walks each component and asks six questions. The acronym is your prompt: every box on the diagram, every arrow, gets each letter applied to it.
| Letter | Threat category | CIA property attacked |
|---|---|---|
| S | Spoofing | Authenticity (a form of Integrity) |
| T | Tampering | Integrity |
| R | Repudiation | Non-repudiation |
| I | Information Disclosure | Confidentiality |
| D | Denial of Service | Availability |
| E | Elevation of Privilege | Authorization |
Walking the diagram
Browser → Web (the arrow)
- S: TLS server cert prevents impersonation. Mitigated by HTTPS.
- T: in-flight tampering. Mitigated by TLS AEAD.
- I: an eavesdropper on the wire. Mitigated by TLS.
- D: floods, slowloris. Mitigated by CDN / rate limit.
Web (the application)
- S: there are no logins, so spoofing of identity is N/A. But the paste ID itself is an identifier – guessable IDs let an attacker "impersonate" any paste. Open question: is the ID random enough?
- T: can a user POST to a URL that lets them overwrite someone else's paste? Open question.
- R: can the operator prove which IP submitted a malicious paste? Logs and retention policy needed.
- I: paste IDs in URLs end up in browser history, Referer headers, logs of intermediate proxies. Open question: do we leak paste content via Referer?
- D: unbounded paste size = disk fill. Open question: what is the max body size?
- E: the admin endpoint – does it exist, is it on the same port, is it authenticated?
Web → DB and Web → Cache
- T / I: are these connections TLS or plaintext on the local network? On a flat VPC, plaintext is fine if the segment is locked down; on a shared network, it is not.
- D: cache eviction on memory pressure may cause cascading DB load.
- E: SQL injection on user-provided text. Open question: prepared statements only?
Not a stack of "vulnerabilities". A short list of open questions that someone in the room cannot answer with confidence, and a plan to close each one. Threats you can already mitigate are noted but not interesting.
Concrete mitigations for our open questions
- Paste ID randomness: 128 bits of entropy from
secrets.token_urlsafe(22). Reject any shorter ID at the router. - Overwrite via POST: ID issuance is the only place a paste is created; the route accepts no
idfrom the client. - Referer leak: set
Referrer-Policy: no-referreron paste view pages. - Body size: cap at 1 MB at the proxy. Return 413 above.
- SQL injection: parameterised queries enforced by the ORM; a CI rule fails the build if a raw SQL string mentions
%s.
STRIDE generalises: a second pass, on a mobile login flow
Doing the framework once on a paste service risks making it look web-app-specific. It isn't. STRIDE is six questions you ask of any diagram with boxes and arrows, and a mobile app's login flow is a genuinely different shape of system: an API instead of server-rendered pages, an OS-level secure keystore instead of cookies, a third-party OTP vendor in the loop. Same six letters, different nouns.
[ Mobile App ] →https→ [ Auth API ] → [ Session store ]
↓
[ SMS/Push OTP provider ]
Rather than repeat the full per-component walk, here is the same exercise condensed into the table shape a reviewer (or an LLM, as below) would actually produce – one standout question per component instead of all six letters:
| Component / flow | Standout letter | Open question |
|---|---|---|
| App → Auth API | Spoofing | Is the connection pinned to a known certificate, or does the app trust any CA-issued cert – including one from a compromised or coerced CA? |
| Auth API | Elevation of Privilege | Are login attempts rate-limited per-account as well as per-IP? Per-IP alone doesn't stop distributed credential stuffing. |
| Auth API → OTP provider | Information Disclosure | Does the OTP vendor log phone number and code together in a way a compromised third party could replay? |
| Session store | Tampering | Are session tokens signed (HMAC or equivalent), so a client can't forge an elevated claim like a different account ID? |
| App (local storage) | Information Disclosure | Is the token in OS-level secure storage (Keychain / Keystore), or sitting in plaintext SharedPreferences a rooted device can read? |
Notice the questions are structurally identical to the paste-service ones – only the nouns changed, from "paste ID" to "session token," from "Referer leak" to "insecure local storage." That is the actual value of STRIDE: it doesn't require domain expertise in mobile security or web security specifically, only discipline in applying the same six letters to whatever is on the diagram in front of you.
Concrete mitigations for the mobile flow
- Certificate pinning: pin the API's certificate, or its public key, inside the app so a mis-issued certificate from any trusted CA can't intercept the connection – and plan for the operational cost of rotating pins when certs renew.
- Per-account rate limiting: throttle failed logins by account identifier as well as source IP, with exponential backoff, so credential stuffing spread across thousands of residential IPs still hits a wall.
- Separate OTP logging: never write the phone number and the one-time code to the same log line or record. If support tooling needs both, require a separate, access-controlled lookup to join them.
- Signed session tokens: use a signed format – a JWT with a server-held key, or an opaque token validated server-side – so a modified client can't silently promote its own claimed privileges.
- OS-level secure storage: store the token in the Android Keystore or iOS Keychain, never in SharedPreferences or UserDefaults, and mark it non-exportable where the platform supports it.
The LLM prompt that gets you 60% there
Threat modeling is mechanical enough that a well-prompted LLM can do most of it. The prompt below has been refined over a dozen real reviews. Plug your own architecture into the placeholder.
You are a security reviewer. I will give you an architecture description. Walk it with STRIDE. For every component and every data flow, output: - Component or flow name - One line per STRIDE letter, marked one of: MITIGATED (and by what) OPEN (and what to investigate) N/A (and why) - Do NOT speculate about implementation details that are not in the description. - At the end, list the OPEN items as a numbered to-do. Architecture: <paste your diagram description here>
Two things matter about that prompt. It forces a per-component table (which an LLM is good at) and it forbids speculation (which an LLM is bad at). The output is a starting point, not a final report – you walk it with the team and turn each OPEN into a "we know" or "we need to fix".
What you have just learned
Threat modeling is not a meeting where you list everything that could go wrong. It is a structured way of finding the things nobody at the table can confidently answer. STRIDE is the cheapest structure that catches most of them. An LLM can scaffold the first draft for you and let you spend the meeting on the answers.
STRIDE isn't the only option
The framework most often mentioned in the same breath as STRIDE is DREAD: but DREAD isn't a competing way to find threats, it's a scoring rubric for ranking the ones STRIDE already found (Damage, Reproducibility, Exploitability, Affected users, Discoverability). The two are commonly used together: STRIDE generates the list, DREAD helps decide what to fix first. The frameworks that actually compete with STRIDE for finding threats in the first place are heavier tools like PASTA and, for narrower questions, attack trees.
| Framework | Approach | Best for | Trade-off |
|---|---|---|---|
| STRIDE | Six threat categories applied to every component and data flow on a diagram | Fast, structured design reviews – an hour, not a week | Doesn't rank threats by business risk on its own |
| PASTA | Seven-stage, risk-centric process tying business objectives to simulated attacks | Larger systems where you must justify prioritisation to non-security stakeholders | Much heavier – days of work, not an hour with a whiteboard |
| Attack trees | One root goal (e.g. "steal customer PII") decomposed into AND/OR branches of how an attacker gets there | Modeling one specific high-value target in depth | Doesn't systematically cover a whole system the way STRIDE's component walk does |
These aren't mutually exclusive. A common pattern is STRIDE as the default for every design review that touches a data flow diagram, with PASTA or an attack tree reserved for the small number of crown-jewel systems – payment processing, the auth service itself – that justify the extra rigor. An attack tree even makes a natural follow-up to a STRIDE finding: once STRIDE flags "elevation of privilege on the auth API" as an open question, an attack tree rooted at "attacker gains admin session" forces you to enumerate every branch that gets there – credential stuffing, a leaked signing key, a vulnerable dependency – rather than stopping at the first plausible path. Start with STRIDE. Reach for the others when STRIDE's output tells you a specific asset needs a deeper look.
Frequently asked questions
What does STRIDE stand for in threat modeling?
How do you apply STRIDE to a real system?
What is the actual output of a threat modeling session?
Can an LLM help with threat modeling?
Daniel A. and Óscar S. run Breachfolio, a small independent site about security and AI. This article was drafted with AI assistance and reviewed by a person before it went live. We write from documentation, vendor sources and published research rather than from original lab benchmarks, and we link a source in the sentence that relies on it. How we work · About us