# What "Security" Even Means (Threat Modeling Basics)

> Security is protecting confidentiality, integrity, and availability against people who want your system to break - and threat modeling is just four plain questions that turn that vague worry into a plan.


---

# What "Security" Even Means (Threat Modeling Basics)

You've probably been told to "make it secure" without anyone explaining what that *means*. So security ends up feeling like a checklist someone else owns, or a wall of jargon - XSS, CSRF, OWASP - that you nod along to and hope nobody asks about. The unease underneath is real: you don't have a way to *think* about it, so you can't tell whether you've done enough.

This guide gives you that way to think. Not a list of attacks to memorize - a mental model you can reason from. By the end you'll be able to look at any system and ask the right questions: what am I protecting, who'd want to break it, how could they get in, and what do I do about it. That's the foundation. The specific holes (and how to close them) come in the guides this one points to.

## How to read this

- **Just need the framework?** Jump to [Phase 2: Threat Modeling, Lightly](02-threat-modeling-lightly.md) for the four questions you can run on any system today.
- **Want it to finally make sense?** Read in order - each phase builds the mental model the next one stands on.

## The phases

1. **[Think Like an Attacker](01-think-like-an-attacker.md)** - what "secure" actually protects (the CIA triad), and the one mindset shift the whole field rests on: from "does it work?" to "how could this be abused?"
2. **[Threat Modeling, Lightly](02-threat-modeling-lightly.md)** - four plain questions that turn vague worry into a plan, plus trust boundaries: where data crosses from a place you don't control into a place you do.
3. **[Defense in Depth & Least Privilege](03-defense-in-depth.md)** - why no single wall is enough, how to give every part the minimum power it needs, and how to assume a breach will happen and limit the damage when it does.

> This guide is the *map*. The specific vulnerabilities - injection, broken auth, the rest - live in [the OWASP Top 10](/guides/owasp-top-10), and the difference between "who are you" and "what are you allowed to do" lives in [auth vs. authz](/guides/auth-vs-authz). Read this first; those will make far more sense afterward.


---

# Think Like an Attacker

Before any tools, any checklists, any acronyms - let's install the one idea the whole field rests on. Most of security feels overwhelming because nobody told you what you're actually defending, or against whom. Once you have those two pieces, the rest stops being magic.

## What "secure" is actually protecting

When someone says "is it secure?", they're really asking about three separate things. Security people bundle them into one famous shorthand: the **CIA triad**. (No relation to the agency - it's just the three first letters.)

```text
        ┌──────────────────────────────────────────────┐
        │  C  Confidentiality  - only the right people   │
        │                        can SEE the data        │
        ├──────────────────────────────────────────────┤
        │  I  Integrity        - only the right people   │
        │                        can CHANGE the data,     │
        │                        and only in allowed ways │
        ├──────────────────────────────────────────────┤
        │  A  Availability     - the right people can     │
        │                        actually USE it when     │
        │                        they need to             │
        └──────────────────────────────────────────────┘
```

📝 **Terminology.** *CIA triad* = the three goals of security: **C**onfidentiality (keep secrets secret), **I**ntegrity (keep data correct and un-tampered), **A**vailability (keep the system working and reachable). Almost every security problem is one of these three breaking.

**What this looks like in real life.** Think about your own bank account, and each letter snaps into focus:

- **Confidentiality breaks** when someone reads your balance and transactions who shouldn't - a leaked database, a borrowed login.
- **Integrity breaks** when someone *changes* the data: edits your balance, redirects a transfer, alters a record after the fact. The data is still there - it's just been *lied to*.
- **Availability breaks** when you can't get in at all - the site is down, you're locked out, the service is being flooded so nobody can use it.

**Why this matters.** "Make it secure" is too vague to act on. "Make sure no one but the account owner can *see* this (confidentiality), no one can *alter* it (integrity), and the owner can always *reach* it (availability)" - that you can actually do something about. The triad turns a feeling into three concrete questions.

💡 **Key point.** When you're unsure whether something is a "security issue," ask: *does it threaten confidentiality, integrity, or availability?* If yes, it's security. If no, it's probably a different kind of bug.

## The mindset shift: from "does it work?" to "how could this be abused?"

Here's the shift that separates building software from securing it - and it's a genuine shift, not a tip.

When you build a feature, you think about the **use case**: the user does the right thing, in the right order, and gets the right result. You test that the happy path works. That's your whole job most days, and it's the correct instinct.

Security asks the opposite question. Not "does it work when used correctly?" but **"what happens when someone uses it *incorrectly on purpose*?"** That's the **abuse case**.

📝 **Terminology.** *Use case* = how a feature is meant to be used. *Abuse case* = how a feature could be deliberately misused by someone who wants it to break. Building is about use cases; security is about abuse cases.

Walk through one concrete example. Imagine a search box that shows results matching what you type. The use case is obvious - you type `shoes`, you get shoes.

```text
   USE CASE (what you built for):
        user types:  shoes
        you show:    results for "shoes"          ✓ works

   ABUSE CASE (what an attacker tries):
        attacker types:  '; DROP TABLE users; --
        what if the box passes that straight into the database?
        attacker types:  <script>steal_cookies()</script>
        what if the box echoes that straight back into the page?
```

*What just happened:* You didn't change the feature - you changed the *kind of input you imagined*. The normal user types a word. The attacker types something engineered to make your code do something you never intended. You don't need to know the exact attacks yet (those are [the OWASP Top 10](/guides/owasp-top-10)). You just need to start *expecting* that some inputs are hostile.

**Why people get this wrong.** It feels paranoid, even a little insulting to the user. But the attacker isn't a user having a bad day - the attacker is a person, or a script, whose *goal* is to find the input you didn't think of. They have time, they have patience, and they only have to be right once. That's the asymmetry at the heart of all security: you have to cover every door; they only have to find one that's unlocked.

🪖 **War story.** Plenty of breaches start with something nobody thought to abuse - a "name" field that accepted a million characters, a "forgot password" form that revealed whether an email was registered, a file upload that happily accepted a file that wasn't an image. None of those broke the happy path. Every one of them was an abuse case nobody asked about.

## Who is "the attacker," really?

It helps to drop the hoodie-in-a-dark-room image. The attacker is rarely a genius targeting *you* personally. Far more often it's:

- **Automated scripts** scanning the entire internet for known weak spots - they don't care who you are, only that a door is open.
- **Opportunists** who notice something is misconfigured and poke at it.
- **Insiders** - someone who already has *some* access and uses more than they should.
- **People tricking *people*** - phishing an employee is often easier than breaking any code (more on that in [Phase 3](03-defense-in-depth.md)).

The point isn't to fear a mastermind. It's to accept that *someone, or something, actively wants your system to behave badly* - and design as if that's true, because it is.

## Recap

1. **Security protects three things - the CIA triad:** **C**onfidentiality (who can see it), **I**ntegrity (who can change it, and how), **A**vailability (can the right people use it).
2. To decide if something is a security problem, ask which of those three it threatens.
3. The core mindset shift is from the **use case** ("does it work when used right?") to the **abuse case** ("how could this be misused on purpose?").
4. The **attacker** is usually a script or an opportunist, not a mastermind - but they only have to find *one* unlocked door, while you have to guard all of them.

Now that you're thinking in abuse cases, the next phase gives you a light, repeatable way to find them on purpose: threat modeling.


---

# Threat Modeling, Lightly

"Threat modeling" sounds like something that needs a whiteboard, a security team, and a two-day workshop. It can be that. But the version you'll actually use - the one that makes you meaningfully safer for almost no effort - is four questions you can answer on the back of a napkin before you build anything.

The point isn't to predict every attack. It's to *stop and think about abuse on purpose*, before the code ships, while changing things is still cheap.

## The four questions

The whole practice, stripped down, is this - and it's the structure the official guidance uses too (source: the [OWASP Threat Modeling Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Threat_Modeling_Cheat_Sheet.html)):

```text
   1. What are we building / protecting?     ← know your system + its valuables
   2. What can go wrong?                      ← who'd attack it, and why
   3. How could they get in?                  ← the attack surface
   4. What are we going to do about it?        ← the defenses, prioritized
```

Let's take them one at a time, with a running example: a small web app where people log in and store private notes.

### Question 1 - What are we protecting?

You can't defend everything equally, so first name what's actually *valuable*. Not the whole system - the parts an attacker would want.

For the notes app:

- The notes themselves (private - **confidentiality**).
- The fact that a note can't be secretly edited (**integrity**).
- The login credentials (the keys to everything else).
- The app staying up so people can reach their notes (**availability**).

📝 **Terminology.** People call these *assets* - the things worth protecting. If a question feels too big, this is where you shrink it: most systems have two or three assets that really matter, and the rest is plumbing.

### Question 2 - Who'd attack it, and why?

Attackers have *motives*. Naming the motive tells you which asset is at risk and how hard someone will try.

- A random script wants any database it can dump and sell → it's after the notes and credentials (confidentiality).
- A jealous ex wants to read *one specific person's* notes → targeted, after confidentiality.
- A competitor wants the service embarrassed and offline → after availability.

You don't need a complete list. You need enough to realize *different attackers want different things*, which tells you what to protect first.

### Question 3 - How could they get in? (The attack surface)

This is the big one. Add up every place where the outside world can touch your system - every input, every door. That total is your **attack surface**.

📝 **Terminology.** *Attack surface* = the sum of all the points where an attacker can try to interact with your system: every form field, URL, API endpoint, file upload, login page, third-party integration, even the people who work there. The bigger it is, the more doors there are to check.

For the notes app, the attack surface includes:

```mermaid
flowchart LR
  login[login form] --> auth[authentication code]
  note[note text box] --> db[(database)]
  upload[file upload] --> store[file storage]
  reset[password reset] --> email[email + account code]
  api[the public API] --> behind[everything behind it]
```

Every arrow is a place to ask your Phase 1 question: *how could this be abused?* The login form could be guessed at. The text boxes could carry hostile input. The password reset could leak whether an account exists. You're not solving them yet - you're *listing the doors*.

💡 **Key point.** A smaller attack surface is safer almost for free. Every feature, endpoint, or input you *don't* expose is one you never have to defend. "Do we even need this door?" is one of the most powerful security questions there is.

### Question 4 - What do we do about it?

Now, and only now, you decide on defenses - and you prioritize. You'll have more risks than time. Spend your effort where a valuable asset (Q1) meets a likely attacker (Q2) through an open door (Q3). The leftover, low-stakes risks can wait or be accepted on purpose.

That's it. Four questions. You can run them in ten minutes, and you'll catch the obvious disasters before they're written into the code.

## Trust boundaries: where the world becomes "yours"

There's one idea that makes Question 3 click, and it's worth its own section because it's the single most useful concept in this whole guide: the **trust boundary**.

**What it actually is.** A trust boundary is the line where data crosses from somewhere you *don't* control into somewhere you *do*. On one side is the untrusted world - the browser, the network, the user, anything you can't vouch for. On the other side is your trusted system - your server, your database, your code.

📝 **Terminology.** *Trust boundary* = the line separating components you control and trust from those you don't. Data crossing *into* the trusted side must be treated as suspicious until you've checked it.

Here's the boundary in the notes app:

```mermaid
flowchart LR
  subgraph untrusted["UNTRUSTED (you don't control this)"]
    browser["browser / user<br/>anyone can send anything"]
  end
  subgraph trusted["TRUSTED (you control this)"]
    server["your server + database"]
  end
  browser -- "request" --> check{{"trust boundary:<br/>validate, authenticate,<br/>sanitize HERE"}}
  check --> server
```

*What just happened:* Everything on the left of that line can be a lie. The browser can be modified. The request can be hand-crafted by a script that never opened your page. So the moment data *crosses* the boundary into your server, you treat it as guilty until proven innocent - you check who sent it, that it's well-formed, and that it's allowed to do what it's asking.

**Why this is the whole game.** Nearly every classic vulnerability is, at heart, *trusting data that crossed a trust boundary without checking it*. You trusted the note text, so a script slipped through. You trusted the login form, so a guessed password got in. You trusted the file upload, so something that wasn't an image landed on your server. Find your trust boundaries, and you've found the exact lines where your checks need to live.

⚠️ **Gotcha.** "But the data came from *my own* front-end / mobile app - surely that's trusted?" No. Anything on the user's side of the boundary can be altered, replayed, or skipped entirely. A friendly button in your app and a hostile script hitting your server look *identical* by the time they reach the boundary. Validation has to happen on the trusted side, every time - front-end checks are for helping well-meaning users, not for stopping attackers.

🪖 **War story.** A very common mistake: an app validates input nicely in the browser (red error text, the works) and then trusts that the server is therefore safe. An attacker who never loads the page - just sends raw requests - sails straight past all of it. The browser checks were real; they were just on the wrong side of the line.

## Recap

1. **Threat modeling is four questions:** what are we protecting, who'd attack it and why, how could they get in (the attack surface), and what do we do about it.
2. **Assets** are the few things actually worth protecting - name them first so you don't try to defend everything equally.
3. The **attack surface** is every point where the outside world can touch your system; shrinking it makes you safer almost for free.
4. A **trust boundary** is the line where data crosses from the untrusted world into your trusted system - and *everything crossing into the trusted side must be checked*, because the other side can lie.

You now know how to find where the danger is. The last phase is about what to actually *do* there - and the principles that hold even after an attacker gets through the first door.


---

# Defense in Depth & Least Privilege

You know what you're protecting, and where the danger crosses into your system. The temptation now is to find the one perfect lock and call it done. This phase is about why that instinct is wrong - and the three principles experienced people reach for instead. They're not techniques; they're *attitudes* that shape every technique you'll ever learn.

## Defense in depth: no single wall is enough

**The principle.** Assume any one defense will eventually fail, and put another one behind it. Security isn't a wall; it's layers, so that getting past one still leaves an attacker outside the next.

**Why one wall fails.** Every lock has a flaw eventually - a bug, a misconfiguration, a leaked password, a clever input nobody predicted. If your whole defense is one wall, the day it cracks an attacker has *everything*. You're betting the entire system on never making a single mistake, and nobody wins that bet forever.

```mermaid
flowchart TD
  attacker([attacker]) --> L1[network locked down]
  L1 --> L2[strong login + 2FA]
  L2 --> L3[least-privilege access]
  L3 --> L4[data encrypted at rest]
  L4 --> L5[logging + alerts]
```

*What just happened:* No single failure is fatal here. An attacker who gets past the login still hits limited access; if they get further, the data they reach is encrypted; and the whole time, logging is quietly raising a flag. Each layer buys time and shrinks what a breach costs.

💡 **Key point.** Defense in depth means you're allowed to be imperfect on any one layer, because you didn't bet everything on it. Not a lower bar - how real systems survive real mistakes.

## Least privilege: minimum access, always

**The principle.** Give every person, every component, every piece of code *the least access it needs to do its job* - and nothing more.

**What it actually is.** When part of your system needs to read the orders table, it gets read access to the orders table. Not write access. Not access to the users table. Not admin. Just the one thing, scoped as tightly as you can make it.

**Why this matters so much.** Combine it with defense in depth's core truth - *something will eventually get compromised* - and least privilege decides how bad that day is. A compromised piece that could only read one table costs you one table; one with admin "to keep things simple" costs you everything.

```text
   TOO MUCH PRIVILEGE:                  LEAST PRIVILEGE:

   report-generator                     report-generator
     └─ admin on entire database          └─ read-only on the 'sales' table

   if it's compromised →                if it's compromised →
     attacker gets the WHOLE database     attacker gets read-only on one table
```

*What just happened:* Same compromise, wildly different outcome. Least privilege doesn't stop the break-in - it makes the break-in *cheap*. The component never needed more than read-on-one-table, so handing it more was pure downside.

⚠️ **Gotcha.** Broad access creeps in because it's *convenient* - it makes things "just work" during development and you mean to tighten it later. "Later" rarely comes, and over-broad permissions are one of the most common findings in any real audit. Scope it tight from the start; widen only when something actually breaks for lack of access.

## Assume breach: limit the blast radius

**The principle.** Don't only plan for *keeping attackers out*. Plan for *when one gets in* - because eventually one will - and design so the damage is contained.

📝 **Terminology.** *Blast radius* = how much damage a single compromise can do. The goal of "assume breach" is to keep the blast radius small: one account, one server, one table - not the whole company.

This ties the first two together: defense in depth assumes a layer will fail, least privilege assumes a component will be turned against you, and "assume breach" makes that the *starting point* of your design rather than a sad surprise. You stop asking only "how do I keep them out?" and start also asking "when they're in, how do I limit what they reach, and how do I find out fast?"

In practice that means: separate systems so one falling doesn't topple the rest, keep logs so you can *see* a breach happening, and rehearse what you'd actually do. You don't need to build all that today - you need to stop designing as if the wall will never be breached.

## Two truths people learn the hard way

### Security by obscurity isn't security

⚠️ **Gotcha.** Hiding how something works is *not* the same as protecting it. A secret URL anyone can guess or stumble onto, a "hidden" admin page with no real login, a homemade scheme kept private - these feel safe right up until someone finds them, and then offer no protection at all.

The plain version: *obscurity can be a thin extra layer, but never the layer you rely on.* A real lock works even when the attacker knows exactly how it works - that's the whole point of a good lock. If your security depends on the attacker not knowing your secret *method*, you've built on sand. (This is why serious cryptography is published openly and still holds: the strength is in the key, not in hiding the design.)

### Humans are the soft target

🪖 **War story.** You can build perfect layers, scope every privilege, and assume breach beautifully - and an attacker can skip all of it by emailing an employee a convincing "reset your password here" link. That's **phishing**: tricking a *person* into handing over access. It works because it doesn't attack your code at all; it attacks the human in front of it.

📝 **Terminology.** *Phishing* = a fake message (email, text, call) designed to trick someone into revealing a password, clicking a malicious link, or approving something they shouldn't. It targets people, not systems.

Security is never *only* a technical problem. The most carefully secured system still has people with keys, and people can be fooled, rushed, or flattered into using those keys wrong. Defense in depth applies here too: assume someone *will* eventually click the bad link, and make sure that mistake doesn't hand over everything - short-lived sessions, a second factor on logins, least privilege so a stolen account can't reach the whole kingdom.

## Recap

1. **Defense in depth** - assume any one defense fails; stack layers so no single failure is fatal.
2. **Least privilege** - give every person and component the minimum access it needs; this is what makes a breach *cheap* instead of catastrophic.
3. **Assume breach** - design for *when* someone gets in, not just *if*; keep the blast radius small and make breaches visible.
4. **Obscurity isn't security** - hiding how something works is at best a thin extra layer, never the one you rely on.
5. **Humans are the soft target** - phishing skips your code entirely, so your layers have to survive a person making one innocent mistake.

## Where to go next

You now have the *map* - the foundation the whole field stands on. The specific holes, and exactly how to close them, come next:

- **[The OWASP Top 10](/guides/owasp-top-10)** - the most common real-world vulnerabilities, named and explained. This is the canonical list of doors attackers actually use.
- **[Auth vs. Authz](/guides/auth-vs-authz)** - the difference between proving *who you are* (authentication) and what you're *allowed to do* (authorization) - two of the most important, most-confused trust-boundary checks there are.

Read those with the mindset you just built, and they'll read like a checklist of abuse cases - exactly what they are.
