OWASP Threat Dragon
OWASP Threat Dragon is a free, open-source threat-modeling tool built around the exact STRIDE methodology the threat-modeling page teaches: draw a data flow diagram, and for every actor, process, data store, and flow you place, the tool narrows the six STRIDE categories down to the subset that's actually applicable to that shape — so you're prompted with the right questions per element instead of starting from a blank checklist each time. The other half of its pitch is where the finished model lives afterward: it saves as a plain JSON file, and its web edition can write that file directly into a connected Git repository, so a threat model can sit in version control next to the service it describes, reviewed in a pull request the same way a code change is.
Imagine a coloring book where every page already has faint pencil lines showing you roughly where the picture goes, based on what kind of page it is — a "dog" page hints at four legs and a tail, a "house" page hints at a roof and a door. You still have to actually draw and color it in yourself, the pencil lines don't do that part — but you're never staring at a totally blank page wondering where to even start. Threat Dragon does that for security questions: a "process" shape gets pencil lines for all six kinds of danger, a "data store" shape gets pencil lines for a smaller, different set, because a store can't be "spoofed" the way an active process can.
What Threat Dragon is, and the problem it solves
☺ Like you're 10: It's a free drawing tool that already knows the security questions to ask about each shape you draw, and it saves your answers as a file you can keep, not a whiteboard photo you'll lose.
Threat Dragon is an OWASP project, originally created by Mike Goodwin and now maintained under the OWASP umbrella by a small group of community contributors rather than a single vendor. OWASP groups its projects into maturity tiers that shift over time as a project's activity and adoption change, so check the current OWASP project inventory if the exact tier label matters to your evaluation — what matters more for this page is that it's been actively maintained for years and ships two interchangeable editions of the same tool: a desktop edition (an Electron app you install and run entirely offline, with no account and no server) and a web edition (a browser-based app, typically self-hosted, that can authenticate against a Git provider and read/write the model file straight to a repository path). Both editions open and save the identical file format, so a model started on a laptop with no network access opens cleanly in the web edition later, and vice versa.
The problem it solves is the one the threat-modeling page names directly: STRIDE is a checklist that only works if someone actually applies all six categories to every element, consistently, and the output has to be something a team can revisit later rather than a whiteboard photo nobody can search or diff. A spreadsheet or a slide deck can hold a threat list, but neither one enforces "did you ask the spoofing question about this specific data store" the way a tool that already knows a data store's applicable category set can, and neither diffs meaningfully in a code review. Threat Dragon's whole design is aimed at closing exactly that gap: structure the diagram, narrow the questions per element automatically, and store the result as a file a version control system already knows how to handle.
Threat Dragon automates coverage, not judgment — precisely the distinction the threat-modeling page draws about STRIDE itself. The tool guarantees you were shown the right category list for every element you drew; it has no idea whether the threat you wrote under "Tampering" is accurate, whether your diagram matches the real system, or whether the mitigation you claimed actually shipped. All three of those remain entirely a human's job.
Building the diagram: the stencil and trust boundaries
☺ Like you're 10: You drag four kinds of shapes onto a canvas — a person, a box that does work, a cylinder that stores stuff, and arrows connecting them — then draw a line everywhere trust changes.
A new model starts as a name, an owner, and a short description, then one or more diagrams underneath it — most systems get a single top-level diagram, with a deeper diagram added only for a component complex enough to deserve its own pass. The stencil offers a small, fixed shape set that maps directly onto the data-flow-diagram vocabulary the threat-modeling page introduces:
- Actor (an external entity) — a person or an external system the diagram doesn't control, like a shopper's browser or a third-party payment gateway.
- Process — a piece of your own system that does work: a service, a function, a background job.
- Store (a data store) — anywhere data rests: a database, a cache, an object bucket, a log stream.
- Flow — the arrow connecting two shapes, representing data moving between them; every flow gets a name describing what actually travels across it.
- Trust boundary — either a boundary box enclosing a zone or a dashed boundary line a flow crosses, marking exactly the places the threat-modeling page says analysis should concentrate: where a flow moves from one level of trust to another.
Every element also carries an Out of Scope toggle with a reason field. That matters more than it looks: a component silently missing from the diagram reads as an oversight nobody can distinguish from "forgot to draw it," while a component explicitly marked out of scope, with a reason, is a documented decision a reviewer can agree or disagree with. Recent versions of the tool also let you pick a diagram's threat-generation ruleset at creation time — STRIDE is the default and the one this page focuses on, but LINDDUN (privacy-focused categories) and a generic empty ruleset with no suggestions are options in the editor if your deployed version includes them; treat that as a detail to confirm against the running tool rather than assume.
STRIDE per element: what the tool narrows down, and what you still write yourself
☺ Like you're 10: Click a shape, and instead of six blank boxes to fill in, you only see the ones that actually apply to that kind of shape — then you still have to write what the danger really is.
Click any element and a panel opens with a "Threats" tab and an "Add Threat" action. Choosing a category there opens a dropdown that's filtered to the categories Threat Dragon's built-in mapping considers applicable to that shape type — the same convention Microsoft's original STRIDE tooling established, and the one most STRIDE-based tools including this one still follow:
| Element type | Categories suggested | Why the others don't apply |
|---|---|---|
| Actor | Spoofing, Repudiation | An external entity isn't code your system runs, so it can't itself be tampered with, read for secrets, denied service, or elevated in privilege by this diagram's logic — only whether it's really who it claims (spoofing) and whether it can deny an action (repudiation) apply. |
| Process | All six — Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege | A process is the one active thing on the diagram that runs code, holds a session, and enforces (or fails to enforce) authorization, so every STRIDE property is in play. |
| Store | Tampering, Repudiation, Information Disclosure, Denial of Service | A passive store isn't an identity that can be spoofed and doesn't itself elevate privilege — but its contents can be altered, read, flooded, or (absent access logging) modified with no record of who did it. |
| Flow | Tampering, Information Disclosure, Denial of Service | Data in motion has no identity to spoof and nothing to escalate — the risk is limited to the data itself being altered, read, or blocked in transit. |
| Trust boundary | None generated directly | It's an annotation marking where analysis should concentrate, not an element that itself does anything — the threats live on the actor, process, store, or flow that crosses it. |
What the tool does not do is write the threat for you. Selecting "Tampering" on a data-store element gives you an empty title and description field, a severity dropdown, and a status dropdown (typically Open, Mitigated, or Not Applicable) — you still supply the sentence that makes it specific to this system: not "tampering is possible" but "an operator with read replica access can modify order rows without the write path's validation running." That specificity is the entire value of the exercise; a generic STRIDE-category label with no detail is functionally a to-do list item with the details missing.
Accepting a suggested category as a finished threat, without rewriting it to name what actually goes wrong in this system, produces a model that looks thorough and tells a reviewer nothing. A process element ships with all six STRIDE categories pre-listed the moment you add threats for it — that's coverage, not analysis. The habit this page keeps returning to: treat the suggested list as the six questions you must not skip, and spend the real effort on the sentence you write under each one you decide is genuinely relevant.
The model file: what actually gets versioned
☺ Like you're 10: Everything you drew and typed gets written into one plain text file — shapes, arrows, and every threat attached to each one — so git can track it exactly like it tracks code.
A saved model is a single JSON document: a summary block (title, owner, description) and a detail block holding one or more diagrams, each diagram a list of cells — one entry per shape and per flow — and each cell carrying its own threats array. Exact field names have shifted across the tool's major versions, so treat the following as illustrative of the shape rather than a guaranteed-current schema:
{
"summary": {
"title": "Checkout Service",
"owner": "payments-team",
"description": "Threat model for the checkout API and its data flows"
},
"detail": {
"contributors": [{ "name": "Rocky Raccoon" }],
"diagrams": [
{
"title": "Checkout — level 0",
"diagramType": "STRIDE",
"cells": [
{
"id": "a1b2c3",
"shape": "actor",
"data": { "name": "Shopper (browser)" }
},
{
"id": "d4e5f6",
"shape": "process",
"data": {
"name": "Checkout API",
"outOfScope": false,
"threats": [
{
"id": "t1",
"type": "Spoofing",
"title": "Client-supplied user-id header trusted without verifying the session",
"status": "Open",
"severity": "High",
"mitigation": ""
},
{
"id": "t2",
"type": "Elevation of Privilege",
"title": "Admin-only discount endpoint reachable without a role check",
"status": "Mitigated",
"severity": "High",
"mitigation": "Role guard added in PR #482; covered by an integration test"
}
]
}
},
{
"id": "g7h8i9",
"shape": "flow",
"data": { "name": "HTTPS request", "threats": [] }
}
]
}
]
}
}Two things about this file matter more than any single field name. First, it's diffable: a reviewer can see exactly which threat changed status from Open to Mitigated, and cite the pull request that fixed it, the same way they'd review a code diff. Second, the web edition's Git integration means the "export" step most tools require doesn't exist here — you point the model at a repository and a path when you create it, and every save is a commit (or a change staged for one), so the model file can live at, say, docs/threat-model.json right beside the service it describes, versioned on the same branch and reviewed in the same pull request as the code change that prompted the update.
Day-to-day workflow
☺ Like you're 10: Mostly you click and drag, not type commands — the few commands that exist are just for installing or self-hosting the tool itself.
Threat Dragon is overwhelmingly a GUI tool; there's no CLI for authoring a model. What there is, is a handful of ways to run the tool itself, and a repeatable sequence once it's open. Folder names, image tags, and exact scripts shift between releases — verify against the project's own README before running these — but the shape holds:
# Desktop edition — no server, no account, works fully offline. # Download the signed .dmg / .exe / .AppImage from the project's GitHub Releases page. # Web edition, self-hosted via the published Docker image: $ docker run -p 3000:3000 owasp/threat-dragon:latest # open http://localhost:3000 — choose "local" storage to skip connecting a Git provider entirely, # or sign in with GitHub/GitLab/Bitbucket to read and write models straight to a repo. # Running from source for development: $ git clone https://github.com/OWASP/threat-dragon.git $ cd threat-dragon/td.vue && npm install && npm run serve # the Vue frontend, dev server $ cd threat-dragon/td.server && npm install && npm start # the Express API the web edition talks to
The routine itself, once it's running: create a model and point it at a storage location, add a diagram and pick a title, drag the actors/processes/stores onto the canvas and connect them with named flows, drag a trust boundary across every place trust actually changes, then click through each element adding threats from its filtered category list and writing the specific sentence under each one, setting a severity and a status. Save, and — in the Git-connected web edition — that write becomes a commit. Open a pull request alongside the code change the model describes, so a reviewer reads the diagram diff and the threats diff in the same review, not a separate document nobody opens. Then, critically, come back to it: the threat-modeling page is explicit that a model nobody revisits degrades from a security control into a historical document, and nothing about saving the file to git changes that — it only makes revisiting it, and seeing exactly what changed since last time, possible in the first place.
Gotchas and failure modes
☺ Like you're 10: Most of the surprises come from the same root cause — the tool only knows about the shapes you drew and the words you typed, never about the real system underneath.
- The diagram is never checked against reality. Threat Dragon has no way to know whether the DFD you drew still matches the deployed system. Unlike a build that fails when code and a manifest disagree, a stale diagram just sits there looking authoritative, and the threat model built on top of it inherits every blind spot the stale diagram has.
- "Mitigated" is a claim, not a verified fact. Marking a threat's status Mitigated records that someone believes a control exists — it does not check that the control shipped, that it's still in the code, or that it actually works. Closing that loop means the mitigation maps to something independently checked: a merge-blocking Semgrep or CodeQL rule, a config scan, a test — not the threat model's own say-so.
- Suggested categories aren't a finished analysis. The per-element filtering narrows which STRIDE questions apply; it never writes the answer. A model where every process element has all six categories pre-listed with generic one-line titles looks complete and tells a reviewer nothing — see the callout above.
- Canvas position data bloats diffs. The JSON stores x/y coordinates for every shape's placement, so reflowing a diagram to make room for a new element — with no threats or content actually changed — still produces a diff full of coordinate churn. Reviewers who don't know to skim past position-only hunks will waste time on them.
- Nothing gates a merge on an unresolved threat. Unlike a SAST or SCA scanner wired into CI/CD as a required check, Threat Dragon itself doesn't fail a build because a High-severity threat is still Open. If you want that enforcement, you write a script that parses the JSON and checks it in the pipeline, or you reach for a threat-modeling-as-code tool built to run in CI from the start (see the comparison below).
- File-schema drift across major tool versions. The desktop-only 1.x line and the later rewrite don't share an identical file schema in every field. A model authored years ago in an older release may need a migration pass before it opens cleanly in a current install — treat a major version upgrade of the tool itself as a migration event, the same caution this course applies to any format that isn't guaranteed stable across versions.
Alternatives and when to choose it
☺ Like you're 10: Other tools ask the same STRIDE questions differently — some draw pictures like this one, some are just text files a computer can check automatically, and one costs money for a bigger library and more integrations.
| Tool | Model | Cost | Best when | Costs you |
|---|---|---|---|---|
| OWASP Threat Dragon | Visual DFD editor, STRIDE categories filtered per element type, model saved as JSON — optionally written straight to a connected Git repo | Free, open source (Apache-2.0) | You want a visual, drag-and-drop tool a non-specialist can read, with the model versioned next to the code it describes | No CLI, no built-in CI gate — enforcing "no Open High-severity threats" is a script you write; diagram-vs-reality drift is never detected automatically |
| Microsoft Threat Modeling Tool | Visual DFD editor with the same STRIDE-per-element convention; Windows-only desktop app | Free | A team already standardized on Windows/Azure tooling end to end | Windows-only; its own file format is less naturally git-diffable, and non-Windows contributors can't open it at all |
| pytm / Threagile (threat modeling as code) | The model is a Python script or a YAML file — no canvas; the tool renders a DFD from the text and can fail a CI run on unmitigated risk | Free, open source | You want the model generated and enforced automatically in a pipeline, and a fully diffable plain-text format matters more than a visual editor | No drag-and-drop authoring; a reviewer unfamiliar with the DSL can't review the source without the rendered output; a steeper curve for non-engineers |
| IriusRisk | Commercial platform: a larger curated threat/countermeasure library, risk scoring, architecture-pattern templates, Jira/pipeline integrations | Paid, with a limited free community tier | An organization needs governance across many teams — a shared countermeasure library and ticket integration, not one team's diagram | License cost, vendor lock-in, and a heavier rollout than opening a free tool and drawing a diagram |
| Whiteboard / spreadsheet | Ad hoc — draw it, list threats in a doc | Free | A single quick session where no durable, reviewable artifact is needed | Not versioned, not diffable, easy to lose, and impossible to gate a merge on |
The practical rule: reach for Threat Dragon when the deliverable needs to be something a whole team can read at a glance and something git can track like any other artifact — it's usually the right default for a single service or team-owned system. Reach for a threat-modeling-as-code tool like pytm or Threagile when the model needs to be generated and enforced automatically as part of a pipeline rather than authored by hand. Reach for a commercial platform like IriusRisk once the problem is organization-wide governance across dozens of teams rather than one team's diagram. See Secure SDLC Gates & the DevSecOps Maturity Model for where a threat-modeling gate fits among the other required checks in a mature pipeline.
Rocky the Raccoon: Drew the checkout flow in Threat Dragon. Clicked the API process, and it handed me all six STRIDE categories to work through — didn't have to remember them myself.
Foxy: So it told you the threats?
Rocky the Raccoon: No — it told me which six questions apply to a process. I still had to write what actually goes wrong. Under Spoofing I put: the user-id header gets trusted with no signature check behind it.
Timmy the Turtle: And the data store you drew next to it — did it get all six too?
Rocky the Raccoon: Four. No Spoofing, no Elevation of Privilege — a store can't pretend to be something else or grant itself more access. Tampering, Repudiation, Information Disclosure, Denial of Service.
Ellie the Elephant: Where does the finished model actually live? I don't want to go hunting for a file on someone's laptop.
Rocky the Raccoon: Committed straight to the repo — docs/threat-model.json, right next to the checkout service. It's in the same pull request as Benny's fix.
Benny the Beaver: I marked the elevation-of-privilege one Mitigated — added the role guard this morning.
Rocky the Raccoon: Marked, not proven. I'm not closing that one until I see the test that actually exercises it — a status field believing itself isn't the same as it being true.
1. What exactly does Threat Dragon's STRIDE-per-element mapping give you, and what does it explicitly not give you? 2. Name the four STRIDE categories that apply to a data store, and explain in one sentence why the other two don't. 3. What does the web edition's Git integration change about the usual "export, then attach to a doc" workflow? 4. Give two distinct reasons a "Mitigated" status in the file shouldn't be trusted at face value. 5. When would you reach for pytm or Threagile instead of Threat Dragon?
Check your answers
- It filters the six STRIDE categories down to the subset applicable to the shape type you clicked, so you're prompted with the right questions per element. It does not write the threat's description, decide severity, verify the diagram matches reality, or confirm a claimed mitigation actually exists — all of that is still a human judgment call.
- Tampering, Repudiation, Information Disclosure, and Denial of Service. Spoofing doesn't apply because a passive store has no identity to impersonate, and Elevation of Privilege doesn't apply because a store doesn't itself grant capabilities — those two properties only make sense for an active process.
- It removes the separate export step: instead of downloading a file and pasting it into a wiki or attaching it to a ticket, the tool writes the model file directly to a connected repository path, so saving the model is committing it, and it can live right beside the code it describes on the same branch, reviewed in the same pull request.
- Any two of: the tool has no way to verify the underlying control actually shipped or still exists in the code; a diagram the status is based on may itself be stale and no longer match the real system; and the status is set by whoever wrote it, with no independent check (a SAST rule, a test, a config scan) confirming the claim is true.
- When the threat model needs to be generated and enforced automatically as part of a CI pipeline — failing a build on an unmitigated risk — rather than authored by hand in a visual editor, and when a fully diffable plain-text format matters more than drag-and-drop accessibility for non-specialist reviewers.