Chapter 01 · Part I · Foundations

Threats, risk & attack surface

Every later chapter in this book answers one question about one kind of attack. This chapter is about the habit of mind that decides which questions matter: what are we protecting, from whom, through which doors, and how bad would it be if we failed. Security people call that habit threat modeling. It is less a document than a way of looking at a design before somebody else looks at it for you.

6STRIDE categories; each one is the violation of a single security property
4questions in the Threat Modeling Manifesto, from "what are we working on?" to "did we do a good enough job?"
0.0–10.0the range of a CVSS severity score, which rates a flaw, not your risk from it

Common mix-up: a vulnerability is not a risk, and a severity score is not a priority. A flaw rated 9.8 out of 10 in a program that no attacker can reach may matter less to you than a 6.5 in your login page that criminals are using this week. Risk is always about your system: who can reach the flaw, how likely they are to try, and what it would cost you.

Before you start: this chapter assumes you know what a network is and how a home network hangs together. If those are hazy, read What a network is and Your home network on networks.jelia.nyc first.

Precise words

Threat, vulnerability, risk

Security arguments go in circles when people use the same word for different things. "That's a big threat" might mean a skilled criminal group, a bug in the code, or the chance of losing a million dollars. The Internet Security Glossary, RFC 4949, pins the words down, and this book uses its meanings throughout.

Start with the thing worth protecting, the asset: customer records, the money in an account, the ability of a booking system to stay up. A threat is a potential for harm to that asset: someone or something with the capability and a reason to cause it. A vulnerability is a flaw or weakness in the design, the code or the way the system is run that a threat could use. An exploit is the specific technique or piece of code that turns a vulnerability into actual access, and an attack is the attempt itself. When an attack, or an accident, actually breaks the security rules, that is an incident.

Risk ties them together. RFC 4949 defines it as an expectation of loss: the probability that a particular threat will exploit a particular vulnerability with a particular harmful result. Notice the three "particulars." Risk is never a property of a bug on its own. A wide-open window is a vulnerability; whether it is a big risk depends on whether it is on the ground floor, what is in the room, and whether anyone has been trying windows on your street.

WordWhat it meansIn the houseIn a web shop
AssetSomething worth protectingThe jewelry boxThe customer database
ThreatA potential source of harm, with capability and motiveA burglar working the neighborhoodA criminal group selling stolen card data
VulnerabilityA weakness that could be usedA back window with a broken latchA search box that builds database queries from raw text
ExploitThe concrete method that uses the weaknessLifting the window from outsideInput crafted to change the query's meaning (Chapter 13)
RiskLikelihood that a threat uses a weakness, times the harmHow likely a break-in is, and what you'd loseHow likely a breach is, and what fines, refunds and trust it costs
ControlAnything that reduces the riskFix the latch, add an alarm, buy insuranceParameterized queries, monitoring, encryption at rest
IncidentA security rule actually brokenThe jewelry is goneRecords were copied out

What does "harm" mean, though? The classic answer is three properties, the CIA triad. Confidentiality: only the right people can read it. Integrity: nobody can change it without permission, and you can tell if they did. Availability: it works when it is needed. Three more come up so often that most threat modelers add them: authenticity (you are talking to who you think you are), non-repudiation (someone cannot credibly deny an action they took), and authorization (each party can do only what it has been allowed to do).

That list of six properties is the key to the most widely used threat checklist, STRIDE, described by Microsoft engineers Loren Kohnfelder and Praerit Garg in 1999. Each letter names one way to break one property. Once you see the pairing you never need to memorize the acronym again.

LetterThreatProperty it violatesPlain exampleTypical control
SSpoofingAuthenticitySigning in as somebody else with a stolen passwordStrong authentication (Ch 04)
TTamperingIntegrityChanging the price in an order on its way to the serverIntegrity checks, signatures, TLS (Ch 02, 03)
RRepudiationNon-repudiation"I never approved that refund," and no log can show otherwiseTrustworthy, tamper-evident logs (Ch 17)
IInformation disclosureConfidentialityAn error page that prints other customers' detailsEncryption, access control, careful error handling
DDenial of serviceAvailabilityFlooding the checkout page until it falls overCapacity, rate limits, filtering (Ch 09)
EElevation of privilegeAuthorizationAn ordinary user reaching the admin-only refund functionAuthorization checks, least privilege
Go deeper: why the words matter in practice

RFC 4949 itself warns that "threat" is frequently misused to mean either a threat action or a vulnerability. The confusion has consequences. A team that says "we fixed the threat" after patching a bug has removed a vulnerability; the people who wanted to get in are still there and will look for the next one. A team that says "the risk is critical" because a scanner reported a critical vulnerability has skipped the part where somebody asks whether the vulnerable component is even reachable.

The same glossary lists four ways to deal with a risk once you have found it, taken from NIST's risk guidance: avoid it (remove the feature or the weakness), limit it (add controls that reduce the likelihood or the harm), transfer it (insurance, or a contract that makes someone else carry the loss) or accept it (decide, in writing, that it is not worth the cost to fix). Accepting a risk is a legitimate decision. Not noticing one is not.

A design activity

Four questions, asked early

Threat modeling sounds like a specialist ritual with special software and long reports. At heart it is four questions, written down in 2020 by a group of practitioners as the Threat Modeling Manifesto:

  1. What are we working on? A model of the system: usually a sketch of the parts, the data, and how they connect.
  2. What can go wrong? The threats, found systematically rather than by whoever has the most vivid imagination in the room.
  3. What are we going to do about it? A decision for each threat: avoid, mitigate, transfer or accept.
  4. Did we do a good enough job? A check that the model matches what was actually built, and that the fixes happened.

The cheapest moment to ask them is before the code exists. Moving a database behind a second network boundary costs a line on a whiteboard during design and a migration project after launch. That is why the manifesto calls threat modeling something you do continuously, alongside design, not a report you write once for an auditor. A good session takes an hour, involves the people who are building the thing, and produces a short list of changes and a sketch that the next person can update.

You also do not need to imagine every possible attacker. It helps to sketch a few threat actors by capability and motive: an opportunist running automated scans across the whole internet; a criminal group after resellable data; a disgruntled insider who already has an account; a well-funded group that wants your organization in particular. Each one sets a different bar. A family photo site mostly needs to beat the opportunist. A payments company has to plan for the criminal group. Naming the actor keeps the conversation honest about how much effort an attack is worth to someone.

Go deeper: other methods, same four questions

STRIDE is a way of answering question 2 for security. Other checklists answer it for other concerns: LINDDUN, from KU Leuven, does the same job for privacy threats such as linkability and identifiability. Attack libraries such as MITRE ATT&CK catalog the techniques real intruders have been seen using, which helps you test whether your list of threats is realistic; Chapter 17 uses ATT&CK to organize detections.

The OWASP threat modeling pages and the OWASP Threat Modeling Cheat Sheet walk through a lightweight process step by step. Adam Shostack's book Threat Modeling: Designing for Security (Wiley, 2014) is the standard long treatment, and the source of the "four question" framing that the manifesto adopted.

What are we working on?

Draw the system, then draw the lines

The usual model is a data-flow diagram (DFD). It has only four kinds of element, and the shapes are conventional so anyone can read one:

  • External entities (rectangles): people or systems you do not control. A customer's browser, an administrator, a partner's payment service.
  • Processes (circles): code you run. A web application, a background job, an admin console.
  • Data stores (two parallel lines): where data rests. A database, a file share, a log.
  • Data flows (arrows): data moving between the other three.

Then comes the step that does most of the work: draw trust boundaries. A trust boundary is a line between parts of the system that trust each other to different degrees, such as the internet and your web server, or your web server and your database. The internet can send your web app anything at all. Your database should only ever hear from your own code.

Every data flow that crosses a boundary from less trusted to more trusted is a place where something must be checked: who is this (authentication), are they allowed (authorization), and is this input what we expect (validation). Flows that stay inside one zone matter too, but the crossings are where attackers enter and where controls give the most value for the least effort. If a sketch has no trust boundaries, it is a network diagram, not a threat model.

With the diagram drawn, STRIDE becomes mechanical. Microsoft's Security Development Lifecycle uses a table called STRIDE-per-element: each kind of element is exposed to a fixed subset of the six threats. You walk the diagram element by element and ask each applicable question.

ElementSTRIDEWhy
External entity✓✓You can't change its insides; it can be impersonated, and it can deny what it did
Process✓✓✓✓✓✓Code you run is exposed to everything
Data store✓(✓)✓✓Data at rest can be changed, read or made unavailable; R applies when the store is a log
Data flow✓✓✓Data in motion can be changed, read or cut off
Instrument 1

STRIDE on a diagram

Here is a small online shop at example.com, drawn as a data-flow diagram with its three trust zones. Click or tap any element or numbered flow (or use the buttons under the diagram) to see which STRIDE threats apply to it under the per-element rules, then tick the controls you would put in place. The tally counts every unmitigated threat in the whole system by category, and how many sit on flows that cross a trust boundary. Try "TLS everywhere" to watch one control close a dozen threats at once.

Ticking a control anywhere ticks it everywhere it applies: "TLS on internet-facing connections" protects three flows at once. Nothing here touches a network; it is a model running in your browser.

Go deeper: what the diagram leaves out, on purpose

A threat model is only as good as its diagram, and real diagrams are bigger: a content delivery network in front of the web app, a message queue, a single sign-on provider, a build pipeline that deploys the code. Each new element brings its own row of STRIDE questions. The build pipeline is a favorite blind spot: if someone can tamper with it, every process it deploys is tampered with too. Chapter 16 is about exactly that.

The per-element table is a starting point, not a law. A data flow can be spoofed if the receiver does not check where it came from, so many teams ask all six questions of anything that crosses a trust boundary. And the table only finds threats to the elements you drew. The most expensive misses are usually elements nobody drew: the forgotten test server, the vendor's remote-support account, the spreadsheet export that lives on a laptop.

Thinking in the attacker's budget

Attack trees and the cheapest path

STRIDE asks "what can go wrong with each part?" An attack tree asks the same question from the other side: "what does the attacker want, and what are all the ways to get it?" Bruce Schneier popularized the idea in a 1999 article in Dr. Dobb's Journal. The attacker's goal sits at the root. Underneath it are the different ways to achieve it, and underneath each of those, the steps it takes, broken down until each leaf is a single concrete action.

Nodes come in two kinds. An OR node is satisfied by any one of its children: to read the database you could steal an admin's login or break the web app or find a stray backup. An AND node needs all of its children: stealing an admin's login only works if you get the password and get past the second factor and can reach the database's network.

Now put a number on each leaf: what it would cost the attacker in money, time or skill. The tree turns into arithmetic. An attacker who is rational about effort pays the cheapest way through, so an OR node costs the minimum of its children and an AND node costs the sum.

cost(OR) = min(children)  ·  cost(AND) = Σ children
An OR node is as cheap as its cheapest option, because the attacker picks that one. An AND node costs the total, because every step has to be paid for. Do this from the leaves up and the root's number is the cheapest way to reach the goal.

A worked example, with the numbers from the instrument below. Logging in as an admin is an AND of $2,000 (phish the password) + $500 (talk past the second factor, which is easy if it is a text message the admin can be tricked into reading out) + $1,000 (get onto the database network): $3,500. Breaking the web app is an OR of a known unpatched flaw ($3,000) and finding a new injection flaw ($15,000): $3,000. Finding a backup is an AND of finding an open storage bucket ($1,500) and reading the unencrypted file inside ($100): $1,600. Bribing an insider: $50,000. The root is the minimum of $3,500, $3,000, $1,600 and $50,000, so the cheapest attack costs $1,600, and it goes through the backup.

That is the lesson attack trees teach better than anything else. The backup was nobody's idea of the front door, and it was the cheapest way in. Fixing the expensive branches first, however satisfying, does not change what the attacker pays. Encrypt the backups and the cheapest path re-routes to the unpatched web app at $3,000. Patch that and it moves to the admin login at $3,500. Defense is the work of raising the minimum.

Instrument 2

Attack-tree cost calculator

The goal is to read example.com's customer database. Toggle defenses and watch the attacker's route jump to the next cheapest branch. Every leaf's cost and chance of success is editable in the table under the tree; the program recomputes the whole tree from the leaves up on every change. Switch to "most likely" to rank routes by probability instead of cost: then an OR node takes its most likely child and an AND node multiplies its children's chances.

Attacker's best route–
Cost of that route–
Chance it succeeds–
Cheapest cost, no defenses–

Go deeper: where the numbers come from, and why they are still useful

Nobody knows exactly what phishing one particular administrator costs. The numbers on a real attack tree are estimates, often just "cheap, moderate, expensive" or "needs special equipment: yes or no." That is fine, because the tree's main job is comparative. If two branches differ by a factor of ten, a rough estimate is enough to see which one to defend first.

Schneier's article also points out that leaves can carry yes/no properties rather than costs, such as "requires physical access" or "legal," and you can prune the tree for a particular attacker. An opportunist will not bribe anyone; a determined group might. Pruning by attacker capability is how the threat actors from the previous section plug into the tree.

Two cautions. Costs along an AND branch only add if the steps are independent; in practice one stolen laptop may satisfy several leaves at once. And probabilities only multiply if the events are independent, which is rarely exactly true. The tree is a thinking tool, not an actuarial table.

Every way in

Measuring the attack surface

The attack surface is the sum of all the places an attacker could try to get in or get data out. For software it has four main parts:

  • Entry points: every interface that accepts input from outside a trust boundary. Web pages and API endpoints, file uploads, open network ports, email that gets parsed, admin consoles, support chat tools.
  • Reachable code: how much code an outsider's input can reach. A feature nobody uses is still attack surface if its URL answers.
  • Exposed data: what an attacker could read or change through those entry points.
  • Privileged identities: every account that can change the system, from administrators to service accounts and API keys. Each one is a door with a key that can be stolen.

Shrinking the surface is often the cheapest control there is, because a door that does not exist does not need a lock, a patch schedule or monitoring. Turn off the legacy API version nobody calls. Remove the admin console from the public internet. Delete customer data you no longer need. Retire the shared "support" account.

A worked example on the shop from Instrument 1. Before cleanup: 3 public web endpoints for customers, 2 old API versions still answering, an admin console on the internet, a file-upload feature used by one partner, 9 administrator accounts and 4 service accounts with database access. That is 7 internet-facing entry points and 13 privileged identities. After cleanup: the two old API versions switched off, the admin console reachable only through an identity-aware proxy (Chapter 11), the partner moved to a separate upload service, administrators cut to 3 named people and service accounts to 2 with narrowed rights. Now 3 internet-facing entry points and 5 privileged identities. No new security product was bought, and the number of things that can go wrong fell by more than half.

Part of the surfaceCount it byShrink it by
Network entry pointsListening services reachable from each zoneDefault-deny filtering between zones (Ch 05)
Application entry pointsRoutes, API endpoints, upload handlers, parsersRemove unused features and old API versions
Reachable codeCode an unauthenticated request can executeAuthenticate first, parse later; fewer dependencies (Ch 16)
Exposed dataRecords reachable through each entry pointKeep less, for less time; split sensitive data out
Privileged identitiesAdmin, service and API-key accountsFewer, named, scoped, with strong authentication (Ch 04)
Go deeper: the surface moves

Attack surface is not a number you compute once. Each release adds routes, each new vendor adds an integration, each new hire adds an account. The OWASP Attack Surface Analysis Cheat Sheet suggests treating it as something you track over time, and flagging changes that grow it, such as a new endpoint that accepts files or a new admin role, for an extra look in code review. That turns threat modeling from an annual event into a reflex attached to every change that matters.

How bad, how likely

Risk is likelihood times impact

You will always find more threats than you can fix. The point of risk assessment is to put them in order. NIST's Special Publication 800-30, Guide for Conducting Risk Assessments (Revision 1, 2012), lays out the vocabulary most organizations use: a threat source with some capability and intent carries out a threat event, which succeeds if there is a vulnerability and the existing controls don't stop it. The likelihood of that, combined with the impact if it happens, gives the risk.

risk ≈ likelihood × impact
Something very likely but harmless, or catastrophic but nearly impossible, is a smaller risk than something fairly likely and fairly bad. On a five-point scale, likelihood 4 and impact 4 scores 16; likelihood 1 and impact 5 scores 5.

Most teams can't put defensible dollar figures on either factor, so they use a qualitative risk matrix: a grid with likelihood on one axis and impact on the other, each scored from 1 (very low) to 5 (very high). Each risk becomes a dot. The top-right corner, likely and severe, gets fixed first; the bottom-left gets accepted and watched.

Matrices are popular because they are quick and they start the right conversation. They also have real limits, and it is worth knowing them before you trust one:

  • The numbers are ranks, not quantities. "Likelihood 4" is not twice "likelihood 2." Multiplying ranks gives a sorting key, not a measurement.
  • Coarse grids create ties and cliffs. Two risks in the same cell can differ tenfold in reality, and a tiny change in a judgment can move a dot across a color boundary. The analyst Louis Anthony Cox Jr. set out these problems in "What's Wrong with Risk Matrices?" (Risk Analysis, 2008).
  • Estimates are biased by what is memorable. The threat in last week's news gets scored higher than the boring one that is actually more likely.
  • The grid hides what controls share. One control often moves several dots at once, which a per-risk ranking does not show. That is why the instrument below lets you apply controls as well as drag dots.

Used with those limits in mind, a matrix is a good way to turn a long threat list into a short, ordered work plan, and to make the decision to accept a risk explicit rather than accidental.

Instrument 3

Sort the risks, then rank the fixes

Eight risks for the example shop sit on a five-by-five grid. Drag a numbered dot to where you think it belongs, or select it with a button and move it with the arrow keys while the grid has focus. The list underneath re-ranks on every move by likelihood × impact. Then apply a control and watch it move several dots at once: that is how one well-chosen fix can outrank three narrow ones.

Fix now (score 15+)–
Plan (8–14)–
Accept and watch (under 8)–
Total of all scores–

    Public scores, private risk

    CVE, CVSS, KEV and your system

    Most vulnerabilities you will deal with are not ones you found yourself. They arrive as advisories about software you use, and three public systems describe them.

    • CVE (Common Vulnerabilities and Exposures, cve.org) gives each publicly disclosed flaw an identifier, like CVE-2024-12345, so that everyone is talking about the same bug. It is a name, not a judgment.
    • CVSS (Common Vulnerability Scoring System, maintained by FIRST; version 4.0 is current) scores a flaw's technical severity from 0.0 to 10.0 from properties such as whether it can be reached over the network, whether it needs a login or user interaction, and what an attacker gains. Scores map to ratings: 0.1–3.9 Low, 4.0–6.9 Medium, 7.0–8.9 High, 9.0–10.0 Critical.
    • KEV, CISA's Known Exploited Vulnerabilities catalog, lists flaws that there is reliable evidence attackers are actually using. It answers a different question: not "how bad could this be?" but "is anyone doing it?"

    None of the three knows anything about your system. The CVSS base score describes the flaw in the abstract, so a Critical rating on a component that is unreachable in your design is less urgent than a Medium on your internet-facing login that appears in KEV. That is the common mix-up from the top of the page, and it is how teams end up patching in score order while the exploited bug waits its turn.

    A practical ordering that follows from this chapter: first, anything in KEV that is reachable from outside a trust boundary; then high-severity flaws on internet-facing entry points; then the same flaws deeper inside, where an attacker would need another foothold first; then the rest by score. Shrinking the attack surface, the previous section's lesson, moves items down this list without patching anything.

    Go deeper: environmental scores and exploit prediction

    CVSS was always meant to be adjusted. Beyond the base metrics it defines threat metrics (is exploit code available or is it being exploited?) and environmental metrics (how important is confidentiality, integrity or availability for this asset, and do your controls change the picture?). The CVSS v4.0 specification describes how these modify the base score. In practice many organizations only ever look at the base number, which is exactly the part that knows least about them.

    FIRST also publishes EPSS, the Exploit Prediction Scoring System, a model that estimates the probability a vulnerability will be exploited in the near future from observed activity. Severity (CVSS), observed exploitation (KEV) and predicted exploitation (EPSS) are three different inputs. Your own exposure, from your diagram and your attack surface, is the fourth, and it is the one only you can supply.

    What follows from all this

    Depth, least privilege, and assume breach

    Look back at the attack tree and the STRIDE tally and two design principles fall out, both old and both still the most useful ideas in the field.

    Defense in depth. Every control fails sometimes: a patch is late, a password is phished, a rule is mistyped. So build layers that fail independently, and arrange them so an attacker has to beat all of them, an AND node in their tree, rather than any one of them, an OR node. RFC 4949 describes it as layered and complementary mechanisms, so that if one is defeated, others behind it still provide protection. Safety engineers draw it as slices of Swiss cheese: every slice has holes, but the holes rarely line up. In the shop, MFA on admin logins, the database on its own network segment (Chapter 5), encrypted backups and alerting on unusual queries (Chapter 17) are four slices.

    Least privilege. Give every person, process and account the minimum access it needs to do its job, and nothing more. Jerome Saltzer and Michael Schroeder listed it among their design principles in 1975, and RFC 4949 defines it the same way today. In STRIDE terms it caps the damage of every E; in attack-tree terms it makes many leaves useless even when they succeed. The web app's database account that can read and write rows but cannot drop tables, the admin role that can refund but not export the customer list, the API key that works for one service only: each one turns a potential disaster into a contained incident.

    Assume breach. Put the two together and you get a mindset: design as though some attacker is already inside, because eventually one will be. That changes questions. Not only "how do we keep them out?" but "if this web server were taken over tonight, what could it reach, and how would we notice?" The answers lead to segmented networks (Chapter 5), access decided per request rather than per network location (Chapter 11), logging and detection that watch the inside (Chapter 17), and a rehearsed plan for when it happens (Chapter 18).

    And finally, rank. You cannot fix everything, so do this in order: shrink the attack surface where it is free; put controls on every flow that crosses a trust boundary; raise the cheapest branch of the attack tree, then the next; patch what is known to be exploited and reachable before what merely scores high; and write down the risks you accept, with a date to look at them again. That last step, revisiting, is the fourth manifesto question in practice: did we do a good enough job?

    Go deeper: where this sits in an organization's security program

    The NIST Cybersecurity Framework (version 2.0, 2024) organizes a whole security program into six functions: Govern, Identify, Protect, Detect, Respond and Recover. Threat modeling and risk assessment are the core of Identify: knowing your assets, your threats and your risks. Every other function depends on it. You cannot protect what you have not identified, and you cannot detect an attack you never imagined.

    The NIST zero trust architecture guide, SP 800-207, is in a sense "assume breach" written as an architecture, and Chapter 11 takes it apart properly.

    Cheat sheet

    Terms from this chapter

    Asset
    Something worth protecting: data, money, a service people depend on.
    Threat
    A potential for harm: an actor or event with the capability to violate security (RFC 4949).
    Vulnerability
    A flaw or weakness in design, implementation or operation that a threat could use.
    Exploit
    The concrete technique or code that turns a vulnerability into access.
    Risk
    An expectation of loss: how likely a particular threat uses a particular weakness, and how bad the result.
    CIA triad
    Confidentiality, integrity and availability: the three core properties security protects.
    STRIDE
    Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege; each breaks one property.
    Trust boundary
    A line between parts of a system trusted to different degrees; flows crossing it need checks.
    Data-flow diagram
    A sketch of external entities, processes, data stores and the flows between them, used as the threat model's map.
    Attack tree
    The attacker's goal at the root, ways to reach it as AND/OR branches, concrete steps as leaves.
    Attack surface
    All the entry points, reachable code, exposed data and privileged identities an attacker could target.
    Defense in depth
    Layered, independent controls, so one failing does not expose the asset.
    Least privilege
    Every entity gets only the access it needs for its job.
    CVSS / KEV
    A flaw's severity score from 0.0 to 10.0, and CISA's list of flaws known to be exploited. Neither is your risk.
    Where this comes from

    Sources