Security Engineering · Threat Modeling

Trust Boundaries in Data Flow Diagrams and Where Threats Localize

How a data flow diagram encodes trust as lines rather than prose, why a boundary is only real where a principal loses authority, and how moving one line changes the entire threat list.

Draw a box for the browser, a box for the API, a cylinder for the database, and a line around the API and the database. That line is the model. Everything else in the exercise, the enumerated threats, the mitigations and the residual risk, is downstream of where the line falls. A diagram whose boundary sits one hop away from the enforcement point produces a threat list that is internally consistent and wrong, and the error is invisible because the drawing still looks like an architecture.

A boundary line is a claim about authority, not about topology

The line asserts that data crossing it moves between two different levels of trust, and that something at the crossing can tell the difference. Both halves matter. Physical or network separation is neither necessary nor sufficient. Two processes on one host can sit on opposite sides of a boundary when they run under different user identifiers and communicate through a channel that reports the peer’s identity; two hosts in one rack sit on the same side when they share a credential that any of them can use.

The mechanism that makes a boundary real is a decision made by a principal with less authority about a request from a principal with more. Concretely, on Linux a Unix domain socket can be opened with the SO_PEERCRED option, which causes getsockopt to return the peer’s process identifier, user identifier, and group identifier as recorded at connect time. A privileged daemon that checks those fields before acting is enforcing a boundary, because the kernel, not the client, supplies the identity. A daemon that instead reads a X-User-Id header from the same socket is not enforcing a boundary, however the diagram is drawn: the claim is produced by the side that is supposed to be constrained.

This is why the diagram’s boxes and the deployment diagram’s boxes are different objects. The deployment diagram records where code runs. The data flow diagram records who is trusted to say what. Threat modeling works on the second, because threats are actions taken by a party the model does not control, and control is a property of authority rather than location.

Element vocabulary and why each type fails differently

The notation is borrowed from structured analysis, where a process transforms data, a data store retains it, an external interactor sits outside the modeled system, and a data flow carries data between the other three. Threat modeling keeps the vocabulary because the four types have genuinely different failure surfaces, and a model that collapses them loses the distinctions that make the analysis useful.

A data flow has no identity. It is a channel, and the only questions that apply to a channel concern what it carries and who can read or modify it in transit. A process has identity in the sense that it holds credentials and makes authorization decisions, so it can be impersonated, subverted, or made to act beyond its intended role. A data store persists beyond the lifetime of any single request, so questions about retention, deletion, and cross-tenant separation apply to it and not to a flow. An external interactor is by definition outside the model’s authority: the model can constrain what it accepts from that party, and nothing more.

The practical consequence is that a boundary crossing is only interesting where at least one endpoint is a process. What the elements hold is a separate question, taken up in assets as security properties, because a crossing only matters in proportion to what is on the far side of it. A flow that terminates in a data store has no enforcement point; whatever protects the store has to be a process in front of it, or a mechanism inside the store that authenticates its own clients. Models that draw boundaries around storage without a mediating process tend to generate mitigations that cannot be implemented, such as “validate input before it reaches the table” when nothing between the two can validate anything.

The same system, three boundary placements

Consider a service that accepts jobs over HTTP, writes them to a queue, and has a separate worker pool that consumes them. There are at least three defensible placements of the trust boundary, and each produces a different threat list.

With the boundary between the API and the queue, the API is trusted and the queue is not: anything in the queue is treated as attacker-influenced input, so the worker must validate the job body, re-check authorization for the operation the job requests, and reject jobs whose parameters do not correspond to a live user. Threats that localize here are job forgery, replay of a previously valid job, and parameter substitution.

With the boundary around both the API and the queue, the queue is inside the trusted zone and the worker inherits that trust. Validation moves to the API, the worker is written as if every message is well-formed, and the interesting threats are the ones that cross the outer line: unauthenticated submission, and queue poisoning by a party that can write to the broker but is not the API.

With the boundary around the worker and the queue but not the API, the API becomes the untrusted side and the worker must treat the broker’s contents as hostile even though the broker is trusted, because the API can put anything into it. This placement is the honest one when the API and the broker share a credential, since a compromise of the API yields the broker anyway.

The three models disagree about where input validation belongs, and all three are drawable. The placement that matches the credential topology is the one whose threats can actually be mitigated, because a boundary that no principal enforces yields threats with no corresponding control.

Nested zones and the common-mode problem

Real systems have several boundaries, nested or overlapping. Industrial control reference models formalize this with zones and conduits: a zone is a group of assets sharing security requirements, and a conduit is the controlled path between zones, subject to its own requirements. The vocabulary generalizes usefully to software, where a zone might be a tenant’s data plane and a conduit the only API that reaches it.

Nesting creates a trap. A boundary that appears to be crossed several times is not several independent checks if one principal enforces all of them. Ten services behind a single authenticating reverse proxy that terminates TLS and injects a user header do not have ten boundaries; they have one boundary and ten consumers of its output. If the proxy is bypassed by a direct connection to a service port, an internal call that skips it, or a health endpoint excluded from the routing rules, every one of those services is now on the untrusted side of its own diagram with no enforcement at all. This is the same failure as a single point of failure, restated in trust terms: redundancy in the drawing is not redundancy in the enforcement.

The countermeasure is structural rather than documentary. Each boundary should be enforced by a distinct principal with distinct credentials, so that compromising one enforcement point does not silently dissolve several lines at once. Where a shared enforcer is unavoidable, the model should record it as one boundary and treat the services behind it as a single trust domain, which is a less flattering diagram and a more accurate one.

What the notation cannot carry

Three properties resist the diagram, and models that pretend otherwise generate confident wrong answers.

The first is ordering. A flow notation says that data moves from A to B; it does not say whether B can act before A finishes, or whether two flows can interleave. Race conditions and time-of-check-to-time-of-use problems live entirely in that gap, and no boundary drawing, however careful, represents them.

The second is volume. A boundary that is correct at one request per minute can be irrelevant at ten thousand per second, because the enforcement mechanism itself becomes the target. Rate limits, connection exhaustion, and queue growth are availability threats that attach to a conduit, not to a flow.

The third is the difference between reachability and authorization. A diagram can show that a path exists from an external interactor to a data store through three processes. It cannot show, without annotation, that the path is authorized for some callers and not others, which is precisely the property that determines whether the path is a threat or the intended design. Authorization models are a separate concern from trust topology, and the honest way to combine them is to annotate each crossing with the principal that enforces it and the check it performs.

A model that records, for every boundary crossing, which principal enforces it and what that principal verifies, carries the information the analysis needs. The per-element enumeration that follows from each crossing is the subject of STRIDE applied per element, and the review that keeps the record from going stale is described in keeping a threat model current. A model that records only the line is a picture of an architecture with the security decisions removed. The line is where the analysis starts; the enforcement claim is what the analysis is about, and the next layer of the exercise is deciding which classes of threat attach to each element on either side of it.

References

  1. Threat Modeling: Designing for Security — Adam Shostack, Wiley, 2014
  2. Structured Design: Fundamentals of a Discipline of Computer Program and Systems Design — Edward Yourdon and Larry Constantine, Prentice Hall, 1978
  3. IEC 62443-3-3: Industrial communication networks — Network and system security — Part 3-3: System security requirements and security levels — IEC, 2013
  4. NIST SP 800-160 Vol. 1 Rev. 1: Engineering Trustworthy Secure Systems — NIST, 2022