Threat modeling an identity system
Before choosing defenses
The team behind the photo application has agreed that its password reset needs to be more secure. Ideas arrive quickly: a puzzle to stop bots, a second factor before every reset, links that expire sooner, an email afterward. Any of them might help. But nobody has said what the reset protects, who might attack it, or where it could go wrong, so there is no way to tell which ideas matter and which only add friction.
Threat modeling is a structured way to answer those questions before choosing defenses. It works on a design rather than on finished code, and it is usually organized around four questions. What are we building? What can go wrong? What are we going to do about it? Did we do a good enough job? The answers are written down, so that others can challenge them and the team can return to them later.
Start with what is being built. The photo application's sign-in asks for an email address and a password, then issues a session cookie. To reset a forgotten password, a customer enters their email address, and the application emails a link containing a reset token, a long random value that should work only once and only briefly. Opening the link lets the customer choose a new password. As How applications communicate showed, each step is a separate request, and nothing guarantees that the requests arrive in the order the designers pictured, or from the person they pictured.
What needs protecting
Begin with the things worth protecting, often called assets: customer accounts and what they hold, and everything that grants access to them, such as password hashes, reset tokens, session identifiers and the key the application uses to send email through its provider. Two kinds of asset are easy to forget. Configuration, such as how long a reset link lasts, can weaken every account at once if someone changes it. Audit history, the record of sign-ins and resets, is what the team will need when something goes wrong, and exactly what an attacker would like to erase.
Then find the entry points, the places where something from outside arrives: the sign-in form, the reset request form, the link in the email and the form that sets a new password. A support agent who can change a customer's email address is an entry point too, even though no web page leads there. Finally, mark where data passes between parts with different owners. Each crossing is a trust boundary, the idea introduced in Trust boundaries, and whatever crosses one must be checked on arrival, because a browser can send any value in any field.
Drawing the flow makes an uncomfortable fact visible. The reset depends on the customer's mailbox, which sits entirely outside the application's control, so whoever can read that mailbox can reset the password. That is a property of the design rather than a bug, and the model should say so plainly. A reset can stand in for every other way of signing in, so it deserves the same care as the sign-in itself, as Enrollment, replacement, and recovery explains.
Who you are defending against
Attackers are often pictured as types, such as a bored teenager or a criminal group, which says little about what a design must withstand. A more useful description is what an attacker can do.
| Attacker | What they can do | What it means for the reset |
|---|---|---|
| Remote guesser | Send many requests from many networks, with lists of leaked passwords | Request resets for many addresses, and learn which ones have accounts |
| Phisher | Persuade someone to open a page and type or approve something | Send a fake reset email that leads to a look-alike page |
| Malware on a device | Run as the customer on a device that is already signed in | Read the reset email as it arrives, or use the existing session |
| Someone holding the device | Use an unlocked phone or laptop, even for a few minutes | Complete a reset through the mailbox open on it |
| Insider with administrative access | Use support or administrative tools as they were designed | Change a customer's email address, then send a reset there |
| Compromised partner | Misuse access that a trusted provider or integration already has | Read every reset link that a compromised email provider carries |
Capabilities make defenses comparable. A passkey defeats the remote guesser, because there is no password to guess, and the phisher, because the browser will not use it on a look-alike site. It does nothing about malware on a computer that is already signed in, because that malware never needs to sign in: it uses the session the sign-in produced. A limit on reset requests slows the guesser and means nothing to someone holding the phone, who needs only one request.
Capabilities also combine. In the fictional incident at Cedar Inc. from Why attackers go after identity, the attacker began as a phisher, relaying an employee's sign-in, then called the help desk hoping an agent would use their administrative access on the attacker's behalf. A model can also say which capabilities are out of scope. The photo application cannot protect a customer whose computer is completely controlled by malware, and saying so openly is better than implying otherwise.
Walking a flow as an attacker
With the flow drawn and the attackers described, walk through it one step at a time, as an attacker would. At each step, ask five questions. What if this step is skipped? Repeated? Done out of order? Done with a changed identifier, such as a different account number? Done by someone else? Each step was designed on the assumption that the previous one happened as intended, and these questions test that assumption.
Start with the reset request, where anyone can type any email address. Done by someone else, the request should achieve nothing on its own: the old password keeps working and the account stays unchanged until the link is used. Repeated, the request can fill a customer's inbox, so it needs limits for each address and each network.
Next comes the link. Here are two ways the application could build it:
https://photos.example/reset?account=48213&token=EXAMPLE_ONLY
https://photos.example/reset?token=EXAMPLE_ONLY
The first carries the account number beside the token. Ask what happens with a changed identifier. If the server believes that number when the new password arrives, a customer holding a valid link for their own account can edit it and reset someone else's. The second link carries only the token. The server keeps a hash of each token with the account it belongs to, its expiry and whether it has been used, and finds the account from that record, so nothing the browser sends can point the reset elsewhere.
The walk does not end when the password changes. Skip the cleanup, and anyone already signed in stays signed in, even though a reset is often how a customer tries to throw an intruder out. A careful design ends the account's other sessions, cancels outstanding reset links, notifies the customer, including at the previous address if it changed recently, and records the reset in audit history. The exercise below applies the same questions to four more situations in this flow.
Walk the reset flow
Simulation. Each item pairs a step in the photo application's password reset with one attacker move. Choose what a careful design does. The customers, records and requests are made up, and answering sends nothing.
Deciding what to fix first
A walk like this produces more findings than a team can fix at once. Impact asks what happens if a threat is carried out: one account or every account, an annoyance or a takeover. Likelihood asks how easy and how attractive it is for the attackers the team described. The account number in the link tops both. Any customer could take over any other account with nothing more than a browser, so it is fixed first. A reset form that reveals which addresses have accounts comes next, because it makes guessing more efficient everywhere. A flood of reset emails is a nuisance, and its limits can follow.
Some threats are reduced and then accepted. The team cannot remove the reset's dependence on the mailbox without a different kind of recovery, so it limits the damage: a reset ends other sessions and notifies the customer, and accounts that later add a passkey could be asked to use it during a reset. What remains is an accepted risk: a decision made by someone with the authority to make it, for a stated reason, with a date or an event that reopens it. A gap that nobody noticed is not an accepted risk.
Flow: password reset, photos.example Reviewed: 2026-09-14
Revisit: when the reset, sign-in methods or email provider change
Threat Impact Likelihood Decision
Account number in the reset link High High Fix now: use the token record
Reset reveals which emails exist Medium High Fix now: same reply for all
Used or older links still work High Medium Fix now: single use, newest only
Inbox flooded with reset emails Low Medium Fix next: limits per address
Someone else reads the mailbox High Low Reduce: end sessions, notify.
Rest accepted by product lead
A record like this goes out of date when the flow changes. Adding sign-in by emailed link, giving support a tool that changes email addresses, or moving to a new email provider each adds entry points or boundaries the old model never saw. Revisit the model whenever the flow changes, and after any incident that shows something it missed. The remote guesser at the top of the attacker table is where Guessing, spraying, and stuffing begins.