Security Engineering · Threat Modeling

Applying STRIDE per Element Instead of per System

Why the six threat classes are property violations, which of them are vacuous for each diagram element type, and how the per-element grid turns a mnemonic into an assignable work list.

The six letters of STRIDE are usually introduced as a list of attack types, which is the least useful way to read them. Spoofing, tampering, repudiation, information disclosure, denial of service, and elevation of privilege are not six kinds of attack; they are the six ways a system can fail to uphold a security property, and each letter names the property that the corresponding failure breaks. Spoofing is a failure of authentication. Tampering is a failure of integrity. Repudiation is a failure of non-repudiation, meaning that an action cannot afterwards be bound to a principal. Information disclosure is a failure of confidentiality, denial of service a failure of availability, elevation of privilege a failure of authorization.

Read as property violations, the taxonomy stops being a brainstorming aid and becomes a partition with rules. Some violations are impossible for some things to commit, and the rules fall out of the definition of the diagram element rather than out of anyone’s judgment.

What each element type can fail at

Data flow diagrams use four element types: processes that transform data, data stores that retain it, external interactors outside the modeled boundary, and the flows that carry data between them. Applying the six property violations to each type gives the following mapping. The properties themselves are the ones the asset inventory decomposes a system into, and the grid is only meaningful when each row can be traced to an asset that someone owns.

ElementSpoofingTamperingRepudiationInfo disclosureDenial of serviceElevation
External interactoryesnoyesnonono
Processyesyesyesyesyesyes
Data storenoyesyesyesyesno
Data flownoyesnoyesyesno

The excluded cells are excluded for structural reasons. A data store has no identity in the model and therefore cannot be spoofed; only a principal can be impersonated. A flow holds no authority and therefore cannot elevate privilege, for the same reason that a pipe cannot be promoted. An external interactor sits outside the system’s authority, so the model can neither protect it from tampering nor keep it available; the applicable failures are the ones the modeled system participates in, namely accepting a false claim from that party and later being unable to show what that party did. Of the twenty-four element-class pairs, the standard mapping leaves fifteen applicable, which means nearly two fifths of a naive checklist would produce findings that cannot be fixed.

That last point is the practical argument for the grid. A finding that no element can own is not actionable. “Denial of service against the external customer” is not a defect in the system under analysis; “denial of service against the request queue that the customer’s traffic fills” is, and it names a process or store with an owner.

Why processes carry all six

A process is the only element that holds credentials, executes logic, and makes decisions, so it is the only one against which all six failures are meaningful. It can run under a stolen identity, have its inputs altered, deny having performed an action, leak what it handles, be starved of the resources it needs, and act with more authority than intended.

The last of these deserves a note, because elevation of privilege is not always independent of the other five. In many concrete systems, the path to elevated authority runs through one of the others: an attacker who spoofs an administrative principal is already elevated, and an attacker who tampers with an authorization configuration has effectively elevated without any single flawed check. Treating elevation as a distinct class is useful because it forces the question of what the process’s authority ceiling is and what enforces it, but the findings often overlap with findings from the other letters, and the duplication should be reconciled rather than counted twice when the list is turned into work.

The authority ceiling is the concept worth extracting. For each process, the model should record the principal it runs as, the credentials it can read from its environment or configuration, and the operations it can invoke. The set difference between the ceiling and the operations the process legitimately needs is the elevation risk, and it is the only part of the analysis that shrinking permissions actually reduces.

Two structural cases make the ceiling larger than the requirement. The first is inherited authority: a process spawned by a privileged parent inherits its identity on Unix unless something explicitly drops it, so a helper that only needs to read one file can hold the authority to rewrite the system’s account database. The second is ambient authority from a shared configuration: a single service credential placed in every container’s environment gives every process the union of what all of them need, and the ceiling of each is then the ceiling of the whole fleet. Both are visible in the model only if the process’s identity is recorded as a field rather than inferred from the box’s label, which is why an element table with an identity column is more useful than a drawing alone.

Data stores and the repudiation cell

The repudiation column for data stores is the one most often treated as noise and most often wrong. The threat is not that the store lies; it is that the store’s records cannot establish what happened, so a principal can deny an action that in fact occurred. This reduces to concrete properties of the store: whether writes are attributed to an authenticated principal or to a shared service account, whether the record is append-only or can be edited by the same principal that writes it, and whether the timestamps come from a source the writer controls.

A table written by an application using a single shared database account satisfies none of these. Every write is attributable only to the application, the application’s credentials can update rows it previously inserted, and the timestamp is produced by the process that stands to benefit from a favorable one. Under the property reading, this is a repudiation finding against a data store, and its remediation is an append-only structure with per-principal attribution, which is a design change rather than a logging configuration.

What the taxonomy does not cover

Three gaps are worth knowing before the grid is trusted as complete.

The first is privacy. Disclosure of personal data is only partially the same as a confidentiality failure: the harms that arise from a lawful collection, an overlong retention period, or a linkage between two datasets that were each individually harmless do not map onto any of the six classes. Frameworks built specifically for privacy threat elicitation extend the taxonomy with categories such as linkability, identifiability, non-compliance, and unawareness precisely because the confidentiality letter does not decompose far enough.

The second is sequencing. Every cell in the grid describes a single-element failure, and a large fraction of real attacks are sequences of individually acceptable states, such as a token that is valid when checked and expired when used. Nothing in a per-element grid expresses ordering, which is why an enumeration based only on it needs a second pass that walks each flow in order and asks what can change between two adjacent steps. The models that do represent interleaving are the state-based ones described under attack nets.

The third is the environment. Availability failures caused by the loss of a dependency outside the diagram, such as a certificate authority, a package registry or a cloud region, are invisible to a grid over elements the diagram contains, because the dependency is not an element. Recording external dependencies as external interactors with explicit availability cells is a partial fix, and it is the honest representation, since the model cannot deny the dependency exists.

Using the grid without generating forty pages

The discipline that keeps the output usable is to enumerate cells, not sentences. The analysis walks each element once, records for each applicable class a single line naming the failure and the mechanism that prevents it, and records the inapplicable cells with the structural reason. The reason column is where the model’s assumptions accumulate, and those assumptions are usually more interesting than the findings, because a line reading “spoofing not applicable, the daemon checks peer credentials from the kernel” states a fact about enforcement that can be verified against the code.

Element-by-element output also survives contact with ranking. Each line has an element, a property, and the asset that property belongs to, which is exactly the input a prioritization step needs. The grid’s purpose is not to find every threat; it is to make the search space explicit and to attach every entry to something that can be changed. What it produces is an inventory of failure modes whose owners are identifiable, and the next stage of the analysis is deciding which of them an adversary would actually pursue.

References

  1. The Threats to Our Products — Loren Kohnfelder and Praerit Garg, Microsoft Interface, 1999
  2. Threat Modeling: Designing for Security — Adam Shostack, Wiley, 2014
  3. A Privacy Threat Analysis Framework: Supporting the Elicitation and Fulfillment of Privacy Requirements — Mina Deng, Kim Wuyts, Riccardo Scandariato, Bart Preneel and Wouter Joosen, Requirements Engineering, 2011
  4. NIST SP 800-154 (Draft): Guide to Data-Centric System Threat Modeling — NIST, 2016