Skip to content

    System Design

    Threat Modeling for Backend Systems: STRIDE on a Real Checkout

    Run a threat model on a backend design: draw a data-flow diagram with trust boundaries, apply STRIDE, rank threats and track them, on ShopStream's checkout.

    By Published 13 min read

    Share

    Standard lessonintermediateBackend System Design

    Before this: Security for Distributed Systems: An Overview

    What you'll learn: By the end of this lesson you will be able to run a threat model on a backend design in four steps: draw a data-flow diagram with trust boundaries, find threats with STRIDE, rank them and choose a response, and check that each response works. You will have a worked threat table for ShopStream's checkout, an attack tree for one attacker goal, and a rule for when to repeat the exercise.

    Before a building opens, an inspector walks the floor plan with a marker. She does not wait for a break-in. She stops at every door, window and loading dock and asks what someone could do there and what stops them. A threat model is that walk for software, done on the diagram before the code exists, when a missing lock costs a sentence to fix instead of a release.

    System Design: Security for Distributed Systems introduces trust boundaries and STRIDE in a short section. This lesson is the full working method, with a complete worked example.

    Sources read on 2026-11-08: the OWASP Threat Modeling Cheat Sheet, the Threat Modeling Manifesto and Microsoft's Threat Modeling Tool documentation. The ranking numbers in the example are this lesson's own illustrative judgements.

    The core idea

    A threat model is a structured answer to four questions: what are we working on, what can go wrong, what are we going to do about it, and did we do a good enough job? These are the questions of the Threat Modeling Manifesto, which defines the activity as "analyzing representations of a system to highlight concerns about security and privacy characteristics", and the OWASP cheat sheet adopts them unchanged.

    Two points from the Manifesto shape everything below. It values "a culture of finding and fixing design issues" over "checkbox compliance", and "doing threat modeling" over "talking about it". The output is not a document for an auditor; it is a list of changes to the design, each with an owner.

    Three examples run through the lesson: a minimal example (one flow checked against all six STRIDE questions), a realistic example (the ShopStream checkout threat model) and a failure example (a feature added after the model was written, in "In practice").

    The four steps

    flowchart LR accTitle: The threat modeling loop accDescr: Four steps in a loop. Model the system, find threats, decide the response, validate the result. A design change sends the team back to the first step. M["1. Model the system"] --> F["2. Find threats"] F --> R["3. Rank and respond"] R --> V["4. Validate"] V -->|"design changes"| M

    Figure 1. The loop. How to read this: the arrow back to step 1 is the point. OWASP says a threat model "should be maintained, updated and refined alongside the system", so a new data flow or integration starts the loop again.

    Step 1: model the system

    OWASP calls data flow diagrams (DFDs) "arguably the most common approach" to modelling. A DFD shows external entities (people and systems outside your control), processes (your code), data stores, data flows and trust boundaries. Microsoft's tool draws an outside user as a square, a web server as a circle and a database as two parallel lines, with trust boundaries as red dotted lines "to show where different entities are in control". OWASP notes that trust boundaries "often represent possible attack points", so every flow that crosses one deserves attention first.

    Step 2: find threats with STRIDE

    STRIDE is a mnemonic of six threat categories. OWASP pairs each with the security property it violates, and Microsoft's documentation defines each category:

    Letter Property violated Meaning (Microsoft) ShopStream example
    Spoofing Authentication Using another user's authentication information An attacker signs in with a stolen shopper password
    Tampering Integrity Malicious modification of data, at rest or in transit A request body carries a lower price
    Repudiation Accounting A user denies an action and nobody can prove otherwise A shopper denies placing an order
    Information disclosure Confidentiality Exposure of information to people not meant to have it One shopper reads another's address
    Denial of service Availability Making a service unavailable to valid users Bots empty stock carts and flood checkout
    Elevation of privilege Authorization An unprivileged user gains privileged access A shopper calls a store-only endpoint

    OWASP advises using the six categories "as prompts while reviewing the system model", recording which components, flows or boundaries each threat affects, and starting with STRIDE for a technical design.

    Minimal example. Take one flow: the browser calling the gateway with POST /checkout. Can someone pretend to be the shopper? Can the body be altered? Can the shopper later deny sending it? Can the response show data it should not? Can the endpoint be flooded? Can a shopper reach a function only a store should? Six questions on one arrow yield six candidate threats; the next section does this for the whole checkout.

    Step 3: rank and respond

    OWASP says ranking "should be based on the mathematical product of an identified threat's likelihood and its impact", while admitting both are hard to estimate and some teams also weigh the effort to fix. For each threat choose a response: mitigate (add a control), eliminate (remove the feature or the data), transfer (hand the risk to another party) or accept (record it, with an owner). OWASP warns that "risk transfer does not itself reduce the likelihood or harmful consequences of an event". Keep a register with status. Microsoft's tool uses Not Started, Needs Investigation, Mitigated and Not Applicable.

    Step 4: validate

    OWASP asks for review with all stakeholders, a check that the threats and responses are complete, and that each mitigation can be tested. Microsoft's walkthrough describes two meetings: one to align on the process and walk through the diagram (where a missing component was found within minutes), and a second for threat review and sign-off. A tester in that story finds corner cases the authors missed.

    Worked example: the ShopStream checkout

    This is the realistic example. ShopStream is the course's educational grocery marketplace; the architecture is a reference design and the scores below are illustrative.

    Scope and assets. The scope is one flow: a shopper checks out a cart and pays. The assets an attacker may want are the shopper's session, the order and its address, stock counts, the payment status, and the logs that mention them. Microsoft observes that teams can start from assets, from attackers or from the software design; this lesson starts from the design.

    ShopStream checkout: data-flow diagram with trust boundariesShopper browser to API gateway (F1 HTTPS); API gateway to Checkout service (F2); Checkout service to Orders database (F3 SQL); Checkout service to Inventory service (F4 reserve); Checkout service to Payment provider (F5 charge); Payment provider to API gateway (F6 webhook)INTERNET: UNTRUSTEDSHOPSTREAM PRIVATE NETWORKTHIRD PARTYShopper browser: external entityShopperbrowserexternal entityAPI gatewayAPI gatewayCheckout serviceCheckoutserviceOrders database: data storeOrdersdatabasedata storeInventory serviceInventoryservicePayment provider: third partyPaymentproviderthird partyF1 HTTPSF2F3 SQLF4 reserveF5 chargeF6 webhook

    Figure 2. The checkout data-flow diagram. How to read this: three zones are three levels of trust. Flows F1 and F6 cross into the private network from outside, and F5 leaves it, so they are the first places to look. F2 to F4 stay inside, but a compromised service could still abuse them.

    The threat table

    Likelihood (L) and impact (I) are scored 1 to 3 (low to high) by judgement, and the score is L times I.

    ID STRIDE Flow Threat L x I Response
    T1 S F1 Credential stuffing or a stolen session token signs in as a shopper 3 x 2 = 6 Limit sign-in attempts, short token lifetimes, extra verification on risky sign-ins
    T2 T F1 Request body carries a changed price, quantity or discount 2 x 3 = 6 Recompute totals on the server from catalogue data; never trust client totals
    T3 E F1, F2 Changing an order id in the URL reads or edits another shopper's order 3 x 3 = 9 Check ownership on every read and write; deny by default
    T4 S F6 Forged webhook claims an order is paid 2 x 3 = 6 Verify the provider's signature, then confirm status with the provider before marking paid
    T5 R F1, F3 A shopper disputes placing an order 2 x 2 = 4 Append-only audit log of who, what, when and which idempotency key
    T6 D F1 Bots hoard stock in carts or flood checkout 3 x 2 = 6 Per-user and per-address limits; reservations that expire
    T7 I F3, logs Personal data in logs or backups is exposed 2 x 3 = 6 Log ids, not names; encryption at rest; restrict backup access; short retention
    T8 T F2, F3 A replayed or double-submitted request creates duplicate orders 3 x 2 = 6 One idempotency key per checkout attempt, enforced by a unique constraint
    T9 E F3, F4 A compromised service reaches the orders database or ledger 1 x 3 = 3 Network policy and per-service credentials with least privilege
    T10 D F5 A slow payment provider ties up checkout threads 2 x 2 = 4 Timeouts, bulkheads and a circuit breaker

    Read the table as a work list. T3 scores highest, so it is fixed first. Six threats tie at 6, so the score alone does not order them; break ties by cost to fix and by how many other threats a control also covers (a strict authorization check helps with T3 and part of T9). T2 and T3 share one design rule: the server decides, the client only asks.

    Most responses above are mitigations. Eliminate appears in a decision the table assumes: ShopStream stores no card numbers (the provider holds them), which removes a class of disclosure threats. That is partly a transfer, and OWASP's caution applies: ShopStream still owns the damage if its own webhook handling is fooled (T4). Accept needs a name and a review date; T9 might be accepted at its residual level once network policy is in place.

    Threat T4 in detail

    Webhooks are the flow most often forgotten, because they arrive from outside yet look like internal traffic.

    sequenceDiagram accTitle: A forged payment webhook and the defence accDescr: An attacker posts a fake paid notification to the webhook endpoint. Without verification the order is marked paid. With signature verification and a status check against the payment provider, the forged request is rejected. participant A as Attacker participant G as Webhook endpoint participant P as Payment provider participant O as Orders DB A->>G: POST paid for order 42 (forged) alt no verification G->>O: mark order 42 paid Note over O: goods shipped, nothing paid else signature and status check G->>G: verify the provider's signature G->>P: ask for the real status of the payment P-->>G: not paid G-->>A: reject end

    Figure 3. Why a webhook is an untrusted request. How to read this: the upper branch is the threat, the lower branch is the mitigation. Checking the signature proves the sender; asking the provider for the real status confirms the claim, so neither one alone is relied on. Use the signature scheme your provider documents.

    An attack tree for one goal

    STRIDE starts from the design. An attack tree starts from an attacker goal and works backwards. OWASP lists attack trees for high-value goals, alongside misuse cases, LINDDUN for privacy threats, PASTA, OCTAVE and VAST. It also says not to "add techniques only to make the model appear comprehensive". For the goal "get groceries without paying":

    flowchart TD accTitle: Attack tree for getting groceries without paying accDescr: The goal is to get groceries without paying. Any one of four branches reaches it: forge a payment webhook, tamper with the price in the request, reuse a discount, or take over a shopper account with a saved payment method. G["Goal: get groceries without paying"] --> A["Forge a payment webhook (T4)"] G --> B["Change the price in the request (T2)"] G --> C["Reuse a discount through a replay (T8)"] G --> D["Take over an account with a saved payment method (T1)"] A --> A1["Defence: signature check and provider status lookup"] B --> B1["Defence: server recomputes totals"] C --> C1["Defence: idempotency key and single-use discount"] D --> D1["Defence: sign-in limits and extra verification"]

    Figure 4. An attack tree. How to read this: any one branch reaches the goal, so each branch needs its own defence. The tags tie the tree back to the threat table, which is how the two techniques complement each other.

    Trade-offs

    Choice Gain Cost Wrong when
    Model each change (one flow or feature) Fast, current, scoped Cross-cutting gaps Nobody keeps the system-level diagram
    STRIDE for every element Systematic, easy to teach Many low-value threats Attackers think in goals, not elements (add attack trees)
    Threat model in the design review Cheap fixes Needs a security-aware person in the room The team lacks that person and a way to ask

    The other side of this topic

    A front-end threat model has different assets and boundaries: cross-site scripting, third-party scripts, where tokens live in the browser and what a malicious extension or a pasted link can do. The same four questions apply, and the two models meet at the API: whatever the browser can be tricked into sending, the back end must treat as untrusted input, as threat T2 above does.

    In practice: a new feature that adds a trust boundary

    This is a realistic scenario on ShopStream, an educational reference design. The figures are illustrative.

    This is a realistic scenario, not a case history.

    The situation

    Store managers ask for an order export: a button that emails a download link to a CSV of the store's orders. The checkout threat model does not cover it, because the export is a new flow with a new boundary: the link leaves ShopStream's control through email, chat and browser history.

    Starting architecture

    ShopStream order export: a new boundaryStore manager to Store dashboard (request export); Store dashboard to Export service; Export service to Object storage (write CSV); Export service to Email (signed link); Email to Store manager (click)SHOPSTREAMStore manager: external entityStore managerexternal entityStore dashboardStoredashboardExport serviceExport serviceObject storage: CSV filesObject storageCSV filesEmail: link leaves our controlEmaillink leaves ourcontrolrequest exportwrite CSVsigned linkclick

    Figure 5. The export flow. How to read this: the amber box is outside ShopStream's control. Whoever holds the link can download the file, so the link is a bearer credential.

    Options considered

    Option Cost Risk
    A. Permanent public link per file Simplest Anyone who sees the link reads every order, indefinitely
    B. Link that expires and requires sign-in as the right store Short sign-in step Residual: a signed-in colleague can forward the file
    C. No link; download only inside the dashboard Smallest exposure Less convenient, no email delivery

    The decision

    B for scheduled exports, C for the full-history export. Each file is scoped to one store, links expire after a short window the team sets, every download is checked against the signed-in store, and every request and download goes into the audit log (T5). The new flows enter the threat table as T11 (disclosure through a leaked link) and T12 (a store requesting another store's file, the T3 pattern).

    Rollout and verification

    Each new row gets a test before release: another store's file is refused, an expired link fails, the audit entry exists. A reviewer who did not write the feature walks Figure 5 against the six STRIDE questions.

    The 2 a.m. failure

    An alert fires on download volume from many unfamiliar addresses for one file: a link was pasted into a public chat channel. The response is to revoke the link family and rotate the signing key, then read the audit log to see which files were fetched and by whom. The design already limits the damage (short lifetimes, a sign-in check). The process lesson is that the model was updated when the feature was proposed, not after the incident.

    Architect's notes

    • Threat model each design change, not only at the start. New data flow, new third party, new store of personal data or new public endpoint: reopen the diagram.
    • Draw the boundary first. Most findings sit on a flow that crosses one.
    • Keep the register next to the code, with a status, an owner and a test per threat.
    • Treat inbound webhooks, links and file uploads as untrusted input whatever the sender claims.
    • Keep the table short: ten ranked threats you fix beat a hundred nobody reads.
    • Pair it with privacy: privacy threats have their own technique, LINDDUN, and personal data also needs the compliance work of the GDPR lesson.

    Common mistakes

    • Starting with a list of threats instead of a diagram.
    • Modelling only the happy path and leaving out webhooks, admin tools and batch jobs.
    • Treating the model as a one-off document that nobody updates.
    • Scoring with false precision and ignoring cost to fix.
    • Writing mitigations ("improve security") that cannot be tested.
    • Having only the authors review their own model.

    Key takeaways

    • A threat model answers four questions: what are we working on, what can go wrong, what will we do, did we do enough.
    • Draw a data-flow diagram with trust boundaries first; flows that cross a boundary get attention first.
    • STRIDE gives six prompts, each tied to a violated property: authentication, integrity, accounting, confidentiality, availability, authorization.
    • Rank by likelihood times impact, then choose mitigate, eliminate, transfer or accept, and record an owner and a test.
    • Attack trees complement STRIDE by starting from an attacker goal.
    • The server decides and the client asks; webhooks and links are untrusted requests.
    • A threat model is maintained alongside the system and reopened when a design change adds a flow or a boundary.

    Practice exercises

    Exercise 1 (45 min). Apply the six STRIDE questions to flow F4 (checkout to inventory) and write three threats not in the table.

    Exercise 2 (1 hour). Draw the data-flow diagram of a system you know, mark the trust boundaries and list the five flows you would examine first.

    Exercise 3 (1 hour). Write a test description for each of T3, T4 and T8: what request you send and what response proves the mitigation works.

    Official documentation

    Ask about this article

    Get answers grounded in this post. AI-generated — based on this article, and may be imperfect.

    Scaled AI Weekly

    Enjoyed this? Get more like it every Monday.

    Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.

    Get new articles by email

    Free. We email a confirmation link first, and you can unsubscribe from any email.

    Join engineers building production AI systems

    Comments