You are currently viewing Threat Modeling a Web Feature: A Practical First Pass

Threat Modeling a Web Feature: A Practical First Pass

A profile page can look harmless until you notice an account ID in its URL. Could changing that ID reveal someone else’s profile? HTTPS won’t answer that question. The server needs to decide who may see each record, and a threat model helps the team spot that requirement before a release.

A threat model records what needs protection, how a system works, where trust boundaries lie, and what might go wrong. It won’t predict every attack. It makes assumptions visible so developers can choose defenses and check that they work.

Start with a system and a decision

Pick a scope you can understand: the profile-viewing feature of a training application, for instance, rather than “the entire company.” Say what decision the model will inform. Is the feature ready for release? Which checks belong on the server? What security tests are needed? Name the people responsible for the feature so unresolved assumptions have an owner.

For a first pass, record:

  • Assets: data or capabilities that matter, such as private profiles, account access, and the ability to edit a profile.
  • Actors: legitimate users, administrators, the application service, and anyone who can reach the public interface. An actor isn’t necessarily an attacker.
  • Entry points: places that accept input, including browser requests, file uploads, and administrative interfaces.
  • Dependencies: databases, identity providers, libraries, and external services the feature relies on.
  • Assumptions: claims that need checking, such as “only the account owner can view this record.”

Include actions as well as stored information among your assets. A service might hold no sensitive document but still let someone change a delivery address or disable a security setting. Those actions need protection too.

Browser requests cross a boundary before reaching account data

Draw data flows and trust boundaries

A rough diagram will do: browser → application server → database, with a separate connection to an identity provider if the feature uses one. Label what crosses each arrow—an account ID, session information, or a database query, for example. Mark the places where the system must verify an input or identity claim before relying on it.

In the profile example, the browser supplies an account ID to the server. It can request a record, but the server must decide whether that user may view it. Hiding a control in the browser interface doesn’t shift the authorization decision there; the client is under the user’s control.

Trust boundaries can also sit inside the system. A background worker might need database access that the public-facing service does not. If both use a database account with broad privileges, that distinction disappears. The diagram needn’t include every implementation detail, only the relationships that affect access or data exposure.

Turn each flow into a specific question

At each boundary, ask what is being claimed, who verifies it, and what happens if verification fails. Then ask which security goal the operation could violate. Confidentiality means only permitted people see data; integrity means data changes only in permitted ways; availability means the service remains usable. Privacy may call for a separate look at what data the system collects, retains, and shares.

For the account ID flow, a useful threat statement would be: “A signed-in user requests a different account ID and receives another user’s private profile because the server checks only that the requester is signed in.” That’s more useful than “hackers might steal data.” It names the entry point, possible failed check, asset, and consequence without claiming the flaw exists.

Describe threats without assuming every flaw is present

Threat modeling looks for plausible failure paths, not confirmed vulnerabilities. A checklist can prompt questions about identity claims, data changes, unintended disclosure, service disruption, and excessive privileges. Frameworks such as STRIDE offer categories, but you don’t need to memorize a taxonomy to write a clear threat statement.

Break each threat into parts so the response stays precise:

  1. Precondition: What access or circumstance would be needed? For example, a signed-in account and knowledge of another account ID.
  2. Failure mode: Which expected control might be absent or ineffective? For example, a missing record-level permission check.
  3. Impact: What would happen if the failure were real? For example, disclosure of private profile details.
  4. Evidence to seek: Which code path, design decision, or authorized test could confirm or rule out the concern?

That separation keeps the diagram from becoming a list of accusations. A developer can point to the server-side permission check, while a tester can verify its behavior in an owned application or authorized lab.

Prioritize and choose responses

Some threats need attention sooner than others. Weigh potential harm, how exposed the entry point is, what access is required, and which controls already exist. Record uncertainty rather than assigning a precise risk score you can’t support. A public endpoint that might expose private records generally takes priority over a cosmetic error message with no sensitive content.

For each important threat, choose a response and an owner. You might change the design, add a control, remove an unnecessary permission, accept a documented residual risk, or gather more evidence before deciding. For the profile feature, the server should authorize access to the requested record on every view request. A matching test would use two accounts in a controlled test environment and confirm that neither can view the other’s private record.

The model itself cannot prove the feature is secure. A sound authorization requirement might still be missing from one endpoint. Tie each response to something checkable—a design rule, code review question, test case, or configuration check—and keep the results with the feature’s design notes so they’re available when the feature changes.

Team compares proposed controls with feature behavior

Revisit the model when the system changes

Revisit the relevant flows when a feature adds an upload, exposes an API, changes identity providers, or starts storing a new kind of personal data. Those changes can invalidate assumptions in the original model. A short update is usually more useful than drawing everything again.

To practice, use a small application you own or a permitted learning lab. Sketch its login and profile-view flows. Circle where the account ID enters the server, then write down the server-side check that decides whether to return the requested record.