What Is Attack Surface?

Attack surface is the total set of points where an attacker could try to get into a system, or get data out of one — every exposed API endpoint, every input field, every open port, every third-party integration with standing access, every account that can authenticate. It's not a single number and it's not a single layer: a system's attack surface spans its network, its application code, and the people who operate it, and all three change independently of each other as the system evolves. The term gets used loosely as a synonym for "risk," but it isn't risk — it's exposure. A large attack surface with strong controls on every point can be safer than a small one with a single unguarded entry, which is exactly why measuring it is a distinct exercise from scoring the threats that use it.

Quick definition: Attack surface is every point where an untrusted actor can interact with a system — send it input, request access, or receive data from it. It grows with every new endpoint, integration, and account, whether or not anyone tracked that it did, and shrinks only through deliberate removal.

The three categories of attack surface

Most attack-surface discussion splits into three overlapping categories, and a system's real exposure is the union of all three, not whichever one happens to have a dashboard:

CategoryWhat it covers
Network attack surface Open ports, exposed services, reachable hosts — anything discoverable by scanning the network from outside (or from an internal segment, for lateral-movement exposure).
Software (application) attack surface API endpoints, form inputs, file uploads, deserialization points, third-party libraries and their own exposed surface — anywhere application code accepts or processes untrusted input.
Human attack surface Anyone who can be phished, socially engineered, or tricked into an action with security consequences — plus every account and credential that grants access, since a compromised account is a human-surface exploit with a software-surface outcome.

A vulnerability scanner mostly sees the network category. A SAST/DAST tool mostly sees the software category. Security-awareness training addresses the human category. None of the three, on its own, produces a complete picture — which is a large part of why attack surface as a whole tends to be underestimated: each team tracking one slice reasonably believes their slice is covered.

Why attack surface grows without anyone deciding it should

Attack surface rarely expands through a deliberate decision. It expands as the byproduct of normal work: a new microservice ships with an admin endpoint nobody remembered to lock down; a staging environment gets stood up for a demo and never gets torn down; a third-party integration is added for one feature and quietly retains broad API scope long after that feature is deprecated; a "temporary" public S3 bucket outlives the migration it was created for. Each of these is a small, individually reasonable decision made under normal delivery pressure. None of them shows up as a line item anywhere. The aggregate is shadow IT and unmanaged surface that grows monotonically unless something actively reverses it — and almost nothing actively reverses it, because removing an endpoint or decommissioning a service is rarely anyone's assigned job, while adding one always is.

This is the core reason attack surface has to be measured on a recurring basis rather than documented once. A DFD drawn at project kickoff accurately describes the attack surface that existed at kickoff. Six months of normal feature work later, it describes something that no longer matches the live system — not because anyone did anything wrong, but because surface growth is a predictable side effect of shipping, and nothing about shipping features naturally updates a diagram drawn months earlier.

Measuring attack surface

Three complementary techniques, each catching what the others miss:

  • Asset inventory. A maintained list of every service, host, and domain that's supposed to exist — the baseline everything else gets compared against.
  • External attack surface management (ASM). Continuous scanning from the outside — the attacker's actual vantage point — to catch what the inventory missed: forgotten subdomains, exposed dev environments, misconfigured cloud storage. ASM tools exist specifically because inventories drift and outside-in scanning catches the drift the inventory can't self-report.
  • Code-level entry-point audit. Enumerating every place application code accepts external input — API routes, form fields, file uploads, webhook receivers, deserialization calls — since this is the layer neither network scanning nor asset inventory reaches.

All three answer the same underlying question — "where can something untrusted reach us" — from different vantage points, and a gap in any one of them is a gap in the measurement, not just a gap in that one tool's coverage.

Attack surface from a threat modeler's perspective

A properly drawn DFD is, among other things, a literal inventory of attack surface: every external entity is a source of untrusted input, and every data flow that crosses into the system from outside is an entry point on that surface. This is one of the more underused properties of threat modeling as a discipline — the diagram most teams build for STRIDE analysis is, with almost no extra work, also the asset-inventory artifact that attack-surface measurement usually has to build separately.

The connection runs in both directions. A DFD that's missing an external entity or a data flow isn't just an incomplete threat model — it's a blind spot in attack surface measurement, since whatever isn't on the diagram doesn't get a STRIDE pass, doesn't get a trust boundary check, and doesn't get flagged when the surface it represents changes. Conversely, every time a new external entity or inbound flow gets added to a system, it belongs on the diagram at the same time it goes live — not at the next scheduled review — precisely because that's the moment the attack surface actually grew.

Reducing attack surface

Reduction is subtraction, and subtraction requires someone to own it, since (as above) nothing about normal delivery work does it automatically:

  • Decommission what's actually unused — old endpoints, deprecated API versions, staging environments left running past their purpose. "Might need it later" is the default answer that keeps attack surface growing; it needs an explicit expiration, not an indefinite exemption.
  • Don't expose internal APIs publicly by default. An endpoint built for service-to-service calls doesn't need a public route just because building one was the path of least resistance.
  • Apply least privilege to every third-party integration, and revisit the grant when the feature that needed it changes — broad scope requested once at setup time is exactly the kind of surface that outlives its justification.
  • Remove default and example accounts, endpoints, and credentials before anything reaches production — these are attack surface with essentially zero legitimate use and well-known default values.
  • Segment aggressively — the Zero Trust principle of treating every internal call as its own boundary directly limits how much of the internal surface a single compromised component can reach, even when the total surface itself doesn't shrink.

Common misconceptions

  • "Attack surface is just open network ports." Ports are the most scannable slice, not the whole picture — application-layer input points and human/credential exposure are frequently larger and less visible.
  • "Reducing attack surface means cutting features users need." Reduction targets what's unused, over-privileged, or forgotten — not the functionality the product depends on. Confusing the two is how "reduce attack surface" initiatives stall against legitimate feature-owner pushback.
  • "It's a one-time audit." Attack surface grows continuously as a side effect of shipping. A measurement taken once describes a system that no longer exists within a few sprints.

Applying this in a threat modeling workflow

  • Treat every external entity and inbound data flow on a DFD as an attack-surface inventory item, not just a diagram element — the two are the same thing viewed from different disciplines.
  • Update the diagram the moment a new integration or endpoint goes live, not at the next scheduled review — that's the exact moment the real attack surface changed.
  • Flag newly added external-facing nodes for a fresh STRIDE pass rather than assuming existing coverage extends to them automatically.
  • Periodically diff the DFD against the live environment — an asset inventory or ASM scan result compared against what's actually drawn will surface exactly the shadow-IT drift this post describes.
  • Give decommissioning the same visibility as shipping — a removed external entity or data flow is worth updating the diagram for too, since an accurate "no longer exposed" is as valuable to the model as an accurate "newly exposed."

Your DFD is already an attack-surface inventory — keep it that way

Every external entity and inbound flow you add in ThreatTree's DFD editor is a real attack-surface item, in the same diagram you're already using for STRIDE analysis — no separate inventory to keep in sync by hand.

Get started free

Not ready to sign up? Get new threat-modeling guides by email instead.