Skip to content
New

DeepSeek V4.1 Flash free for 7 days: 10M tokens a day after any top-up. Until Nov 30.

How it works

Threat Modelling with STRIDE: A First Model of a Web App

Learn data flow diagrams, trust boundaries and the six STRIDE categories, then model a local OWASP Juice Shop in Threat Dragon.

Intermediate11 min readBy NotHoly

What STRIDE is

STRIDE is a list of six threat categories: spoofing, tampering, repudiation, information disclosure, denial of service and elevation of privilege. Microsoft uses it in its threat modeling guidance. STRIDE does not find threats for you. It gives you six questions to ask about each part of a design, so the result does not depend on what you happen to remember on a given afternoon.

The method comes earlier than most security tools. A scanner looks at a running system. A threat model looks at the plan: which components exist, what data moves between them, and where the level of trust changes. You can start it with paper and a pen before any code exists.

Why it matters

Many web security failures are design failures. The server trusts a price that the browser sent. An admin route checks that a user is logged in but never checks that the user is an admin. A log file stores session tokens. Each of these can be patched in code, but the code often matches the design it was given. The design is where the problem starts.

The OWASP Threat Modeling Cheat Sheet makes the same point. You need to understand data flows, trust boundaries and the other parts of the system before you can tell which threats apply. A threat model writes that understanding down in a form other people can review.

The building blocks

Data flow diagrams

A data flow diagram (DFD) has four kinds of element:

  • External entities: people or systems outside your control, such as a shopper's browser.
  • Processes: code that receives, changes or sends data, such as a web API.
  • Data stores: places where data rests, such as a database or a log file.
  • Data flows: arrows that show data moving between the other three.

Keep the diagram to one page. If it needs a second page, you are probably drawing two systems.

Trust boundaries

A trust boundary is a line where data passes from a place with one level of trust into a place with another. Your server sits inside the line. A browser sits outside it, because the person using it can change any request before it reaches you. Every flow that crosses a boundary needs validation, and most of the interesting threats sit on those crossings. The OWASP cheat sheet describes these crossings as likely attack points.

Boundaries are not only network edges. A process that runs under a different operating system account, a third-party identity provider, or a database that accepts connections from several services can each be a boundary.

The six categories

Each category breaks one security property.

Category Property it breaks Question to ask
Spoofing Authentication Can someone pretend to be another user, service or component?
Tampering Integrity Can data change in transit or at rest without anyone noticing?
Repudiation Accountability Can someone deny an action, with no record that proves otherwise?
Information disclosure Confidentiality Can someone read data they should not see?
Denial of service Availability Can someone make this component slow or unavailable?
Elevation of privilege Authorization Can someone do more than they are allowed to do?

These match the descriptions in Microsoft's threat modeling documentation. Spoofing, for example, means using another user's authentication information.

Not every category applies to every element. A common starting map, used in many threat modeling tools, is this: external entities get spoofing and repudiation; processes get all six; data stores get tampering, repudiation, information disclosure and denial of service; data flows get tampering, information disclosure and denial of service. Treat the map as a prompt. If a category seems irrelevant to an element, write down why, so a reviewer can challenge it.

How it works, step by step

  1. Define the scope. Write one sentence that names the system, the version and what is out of scope. "The Juice Shop web app and its API, running locally, excluding the host operating system" is enough.
  2. Draw the data flow diagram. Add the external entities, processes, data stores and flows. Label each flow with what it carries.
  3. Draw the trust boundaries. Put a line wherever trust changes hands.
  4. Walk each element through the categories. For every category that applies, ask the question from the table and write the threat as a short sentence. A useful shape is who does what and what goes wrong: "A stolen session token lets an attacker place orders as the shopper."
  5. Choose a response for each threat: a mitigation, a risk you accept with a written reason, or a transfer to another party. A threat with no response is still open.
  6. Save the model in version control next to the code and set a date to revisit it.

For a small feature, steps 1 to 5 can take an hour. The first time, expect to redraw the diagram at least once. That is normal.

Hands-on: model OWASP Juice Shop

OWASP Juice Shop is an intentionally insecure web application for training. In this lab you do not exploit it. You draw its design, list threats and write mitigations. The threats come from reading the design, not from attacking the application.

Run the lab only on your own machine, bound to 127.0.0.1. If you have not set up the container yet, follow OWASP Juice Shop with Docker first.

Prerequisites

You need:

On the test machine used for this article, git --version printed:

text
git version 2.49.0.windows.1

Step 1: start the lab

bash
docker run --rm -d --name juice-shop-lab -p 127.0.0.1:3000:3000 bkimminich/juice-shop

The -p 127.0.0.1:3000:3000 part publishes the port on the loopback interface only, so other computers on your network cannot reach it.

Confirm that the web app answers:

bash
curl -sI http://127.0.0.1:3000/ | head -n 1

On the test machine, the first line of the output was:

text
HTTP/1.1 200 OK

Confirm that the API answers. This is a plain GET request for a product list:

bash
curl -s -o /dev/null -w "products %{http_code} %{content_type}\n" http://127.0.0.1:3000/api/Products

Output:

text
products 200 application/json; charset=utf-8

Check the version the application reports:

bash
curl -s http://127.0.0.1:3000/rest/admin/application-version

Output:

text
{"version":"20.3.0"}

That version is what the image reported on 8 November 2026 on the test machine. Your image may report another one. Write down the version you see, because the threat model describes a specific release.

Step 2: install Threat Dragon

Download the desktop build for your operating system from the releases page, install it and open it. The menu names in the steps below were not checked against a Threat Dragon build for this article, and they change between releases. Match the names to what your build shows.

Step 3: draw the diagram

Create a new threat model and add these elements:

  • An external actor named Browser, described as "shopper's browser, outside our control".
  • A process named Juice Shop web and API, described as "web app and API at 127.0.0.1:3000".
  • A data store named SQLite database, described as "application data file".
  • Two flows from the browser to the process: HTTP requests, and HTTP responses back.
  • Two flows from the process to the store: SQL queries, and the rows returned.
  • A trust boundary that separates the browser from the process and the store.

The model should look like this:

text
Browser (external actor)
   |  HTTP requests and responses
   v
=================== trust boundary ===================
   Juice Shop web and API (process, 127.0.0.1:3000)
   |  SQL queries and rows
   v
SQLite database (data store)

The text sketch is only a guide. The real file is what Threat Dragon saves.

The model uses SQLite as the data store. Check the project's documentation for the version you run, because storage details can change between releases.

Step 4: list threats for each element

Apply the starting map from earlier. The browser is an external entity, so it gets spoofing and repudiation. Each browser-to-API flow gets tampering, information disclosure and denial of service. The API process gets all six categories. The SQLite store gets tampering, repudiation, information disclosure and denial of service.

Here is one example per category. A real model has more rows for each element.

Category Element Threat Mitigation
Spoofing Browser and API session A stolen token lets someone act as a shopper Short token lifetime, signature check on every request, invalidate tokens at logout, rate limit logins
Tampering Browser to API flow A shopper changes the price or quantity in an order request The server recalculates prices from the database and never trusts client totals; validate the request schema
Repudiation Order process A shopper says they never placed an order, and no record shows otherwise Server-side audit log of order events with user ID, timestamp and request ID; no secrets in the log
Information disclosure API to browser flow An error response reveals stack traces or internal fields Return only fields from an allowlist; generic error messages to the client; full details only in server logs
Denial of service Search endpoint Many expensive search requests slow the service for everyone Rate limits per client, maximum query length and page size, timeouts on database queries
Elevation of privilege Admin functions A normal user calls an admin endpoint after changing a role value Authorization check on the server for each request, deny by default, role read from the server-side session

Use the project's documentation to see which weaknesses your version includes on purpose. Describe each threat in design terms. Do not write exploit steps into the model. This lab needs no attack traffic at all.

In Threat Dragon, add each threat to the element it belongs to, with a title, a description and the mitigation. If your build has a status field, set it to open for mitigations you have not built yet.

Step 5: export and commit

Export the model as JSON from the file menu and name the file juice-shop-threat-model.json. Then, in your lab folder:

bash
git init
cp /path/to/juice-shop-threat-model.json .
git add juice-shop-threat-model.json && git commit -m "First STRIDE model"

Git prints a short summary with the branch name and the commit message. The exact text depends on your Git version and settings. If Git asks for an identity, set your own with git config user.name and git config user.email, and commit with your own name.

Before you commit, read the file. It should contain no real hostnames, tokens or customer data. This model only uses 127.0.0.1.

Stop the lab when you finish:

bash
docker stop juice-shop-lab
curl -s -o /dev/null -w "%{http_code}\n" --max-time 2 http://127.0.0.1:3000/

On the test machine, the output was:

text
juice-shop-lab
000

The first line comes from docker stop. The 000 means curl got no answer from the port, so the lab is down.

A threat model is a living document

A threat model describes a design on the day you wrote it. When the design changes, the model is out of date. Common triggers include:

  • a new endpoint or a new role
  • a new login provider
  • a database moved to another service, which adds a network flow and possibly a new trust boundary
  • a new type of data, such as payment details or personal data
  • a dependency that now runs with more access than before

The Threat Modeling Manifesto says that threat modeling should follow design changes, in iterations, each scoped to a manageable part of the system. In practice, this means three habits. Keep the JSON file in the repository next to the code. Add a review question to pull requests that touch authentication, data flows or external integrations: does the threat model still match? Record the date and the application version the model describes. Threats marked as mitigated are still useful history, so keep them.

Common mistakes

  • Drawing the system you wish you had. Draw the one that runs, including the proxy, the cache and the background jobs.
  • Starting with the whole company. Start with one feature and one flow, then expand.
  • Listing threats without mitigations or owners. A threat with no response is still open.
  • Writing a mitigation like "use encryption" with no detail. Say what is protected, where, and how someone checks it.
  • Leaving out the arrows. Most threats live on flows and at boundaries, not inside the boxes.
  • Treating the six categories as a checklist to finish. They are prompts. The useful output is a decision for each threat.
  • Probing systems you do not own to confirm a threat. The model needs no live testing, and any testing belongs in your own authorized lab.

How to go further

  • Read the Microsoft threat list and the OWASP cheat sheet side by side. Note where they agree on the categories and where the wording differs.
  • Model one feature of a project you maintain, such as login or password reset.
  • Continue to OWASP Top 10 for developers. It lists the web weaknesses you will meet most often, and they map well onto the rows of your table.
  • Learn that other methods exist, such as attack trees and privacy-focused methods, for questions that STRIDE answers less directly.

Sources

Sources

  1. Microsoft Learn: Threat modeling tool threats
  2. OWASP Threat Modeling Cheat Sheet
  3. OWASP Threat Dragon
  4. Threat Modeling Manifesto
  5. OWASP Juice Shop project
  6. Docker port publishing

More in Security fundamentals

Security fundamentals

What Is Cybersecurity? A Guide for Beginners

What cybersecurity covers, the CIA triad, common ways systems fail, and a safe first exercise with hashes on your own machine.

Beginner5 min read