Skip to content

Overview

BoxOffice leaves the laptop and goes to production in the cloud. Within a month, four stories land on the team's desk. A contractor's laptop is stolen, and on it is a .env file with the payment provider API key. The new mobile app needs users to log in, and someone proposes "just store the password in the app". At 10:00 on the day a stadium show goes on sale, bots grab 30% of the seat holds in the first minute and resell the tickets at three times the price. And a vulnerable library in the search service lets an attacker run code inside one pod β€” from where, in the default setup, every other service, the database and the Internet are one connection away.

None of these is a bug in a single line of code. They are design problems: where the network boundaries are, who is allowed to talk to whom, how identities are proven, where secrets live and how fast they can be revoked. This class treats security and networking the way the rest of the course treats scalability β€” as architecture decisions with trade-offs, made on purpose, drawn on the diagram, and defended in an interview.

The story so far

Chapter 12 of the story of the course. The gateway already checks every fan's token once (class 4 β€” Identity, built in lab 4), and class 11 replaced Compose with Kubernetes and a delivery pipeline. Now many services, built by many people and by a pipeline, share one platform β€” and the four stories above show what that trust costs: bots in the on-sale line, a leaked key, a pod that can reach everything, libraries nobody reviewed. This class answers: where are the trust boundaries, who may talk to whom, how are secrets limited and revoked, and how do we know what we ship?

Learning objectives

By the end of this class you should be able to:

  • Build a threat model of a BoxOffice flow with STRIDE, and map each threat to a control and to the OWASP API Security Top 10.
  • Design a cloud network for BoxOffice: VPC, CIDR plan, public/private/isolated subnets across zones, route tables, NAT gateway, security groups and private connectivity.
  • Explain what a service mesh adds (mTLS, traffic policy, telemetry), compare sidecar, ambient and eBPF data planes, and write an authorization policy between two services.
  • Choose the right OAuth 2.1 / OpenID Connect flow for each client (mobile app, web app, batch job), and validate a JWT correctly at the gateway.
  • Apply Zero Trust to service-to-service traffic, design the lifecycle of a secret (the payment API key), and secure the software supply chain with SBOMs and signed images.

Roadmap

  1. This overview β€” security as design: threat modeling with STRIDE, defense in depth, and the OWASP API Security Top 10 mapped to BoxOffice. Identity β€” tokens, OAuth 2.1 and OIDC β€” was taught where BoxOffice first needed it, at the gateway: class 4 β€” Identity.
  2. How the Internet works β€” layers, routing hop by hop and BGP, IP addresses and CIDR, public vs private addresses, NAT, subnets and the default gateway; two simulators (address explorer, packet walk through the BoxOffice VPC).
  3. Cloud networking β€” VPC, subnets, route tables, Internet and NAT gateways, security groups vs NACLs, private endpoints, VPN and dedicated links.
  4. Service mesh β€” data plane and control plane, sidecar vs ambient vs eBPF, mTLS, authorization and traffic policies.
  5. Zero Trust, secrets & supply chain β€” never trust the network, workload identity, secrets management, SBOM, image signing and SLSA.
  6. Quiz β€” check your understanding.

Slides

Click the slides and use ← β†’ to move through them; F goes full screen and P opens the presenter view, with the notes. Open the slides in a new tab


Security is a design property

A common mistake is to treat security as a checklist applied at the end ("add HTTPS and a login page"). By then the expensive decisions are already made: the database sits in a subnet with an Internet route, every service shares one database password, the gateway is the only place that checks anything. Changing those later means re-architecting.

The alternative is to ask security questions while drawing the architecture, with the same seriousness as "how many QPS?". Three ideas organize the whole class:

Idea Question it answers Where in the class
Threat modeling What can go wrong, and where? this page
Defense in depth If one control fails, what stops the attacker next? this page, every subpage
Least privilege / Zero Trust What is the minimum each user, service and network path needs? Zero Trust, Service mesh

Two words used throughout:

  • Attack surface β€” the sum of all the points where an attacker can try to enter or extract data: public endpoints, open ports, dependencies, people with access.
  • Blast radius β€” what an attacker can reach after one component is compromised. Good design keeps it small.

Threat modeling with STRIDE

Threat modeling is a structured conversation about a design: draw how data flows, mark where the level of trust changes, and ask systematically what could go wrong at each element1. Adam Shostack summarizes it in four questions2:

  1. What are we working on? β€” draw a data-flow diagram (DFD).
  2. What can go wrong? β€” enumerate threats, for example with STRIDE.
  3. What are we going to do about it? β€” pick a control for each threat (mitigate, accept, transfer, eliminate).
  4. Did we do a good job? β€” review, test, repeat when the design changes.

Step 1 β€” the checkout data-flow diagram

A trust boundary is a line where data moves between components with different levels of trust: the Internet and our edge, our cluster and the payment provider, a service and its database. Threats concentrate on the boundaries.

flowchart LR
    subgraph internet ["Internet (untrusted)"]
        M(["Mobile app"])
        B(["Bot"])
    end
    subgraph edge ["Edge (public subnet)"]
        W["WAF + API gateway"]
    end
    subgraph cluster ["BoxOffice cluster (private subnets)"]
        O["order"]
        I["inventory"]
        P["payment"]
        A["auth"]
    end
    subgraph data ["Data (isolated subnets)"]
        DB[("PostgreSQL")]
    end
    subgraph ext ["Payment provider (3rd party)"]
        PSP["PSP API"]
    end
    M -->|"POST /orders + token"| W
    B -.->|"POST /holds Γ— 10 000"| W
    W --> O
    O --> I
    I --> DB
    O --> P
    P -->|"API key, via NAT"| PSP
    PSP -.->|"webhook: payment captured"| W
    M -->|"login"| A

Five trust boundaries are crossed in a single checkout: Internet β†’ edge, edge β†’ cluster, service β†’ database, cluster β†’ provider and provider β†’ cluster (the webhook). Each arrow is a question: who is calling, how do we know, and what can they do?

Step 2 β€” STRIDE

STRIDE is a mnemonic created at Microsoft for six categories of threat, each the violation of a security property1:

Threat Violated property BoxOffice example Control Where
Spoofing Authentication an attacker calls payment pretending to be order; a forged webhook says "payment captured" OAuth tokens, mTLS between services, webhook signature (HMAC) Identity, Service mesh
Tampering Integrity the price is changed in the request body; a malicious image is pushed to the registry server-side pricing, TLS, signed images Zero Trust
Repudiation Non-repudiation a customer says "I never bought these tickets" and we cannot prove otherwise audit log with user id + trace id, signed events Observability
Information disclosure Confidentiality GET /orders/1042 returns another user's order; the API key leaks in a log object-level authorization, secrets manager, log masking Identity, Zero Trust
Denial of service Availability bots open 100 000 seat holds and nobody else can buy WAF, rate limiting, waiting room, hold TTL Edge
Elevation of privilege Authorization a normal user calls POST /admin/events; the compromised search pod reaches the database function-level authorization, NetworkPolicy, least privilege Zero Trust, Cloud networking

STRIDE is applied per element of the DFD: processes are exposed to all six; data stores mainly to tampering, information disclosure and denial of service; data flows to tampering, disclosure and DoS; external entities to spoofing and repudiation2.

Step 3 β€” rate the risk and decide

Not every threat deserves the same effort. A simple practice is to estimate likelihood Γ— impact (low / medium / high) and act from the top of the list. The output of a threat-modeling session is a short table of threat β†’ decision β†’ owner, kept with the design document and revisited when the design changes.

BoxOffice β€” three decisions from one session

Threat Likelihood Γ— impact Decision
Bots hoard seat holds at 10:00 high Γ— high Mitigate: WAF bot rules + per-account and per-IP rate limits + waiting room + 10-minute hold TTL
Forged "payment captured" webhook medium Γ— high Mitigate: verify the provider's HMAC signature and re-query the payment status before issuing tickets
Provider outage leaks no data but blocks sales medium Γ— medium Accept for now; revisit with a second provider next quarter
Think first: where is the most valuable asset in BoxOffice?

Not the database of events β€” that is public information. The most valuable assets are the payment provider API key (whoever has it can issue refunds or charges), customer personal data (regulated by Brazil's LGPD, the General Data Protection Law), and the seat inventory itself during a flash sale (a business asset that bots try to steal). The controls should be strongest around them.


Defense in depth

No single control is perfect: WAF rules have gaps, tokens get stolen, a library ships with a remote-code-execution bug. Defense in depth stacks independent layers, so that an attacker who defeats one still faces the next. The layers of BoxOffice, from the outside in:

flowchart LR
    U(["Client"]) -->|"1 Β· TLS, WAF, rate limit"| GW["API gateway"]
    GW -->|"2 Β· validate token (OAuth/OIDC)"| O["order"]
    O -->|"3 Β· check ownership (authZ)"| O
    O -->|"4 Β· mTLS + mesh policy"| P["payment"]
    P -->|"5 Β· subnet + security group"| DB[("PostgreSQL")]
    P -->|"6 Β· short-lived secret"| PSP["PSP"]
# Layer Main threats Controls Subpage
1 Edge DDoS, bots, injection, eavesdropping TLS, WAF, rate limiting, waiting room Edge
2 Identity stolen or forged credentials OAuth 2.1 / OIDC, short-lived tokens, MFA, passkeys Identity
3 Request authorization user reads someone else's data ownership, roles, scopes checked in each service Identity
4 Service to service lateral movement, spoofed callers mTLS, mesh authorization policy Service mesh
5 Network reaching what should be unreachable private subnets, security groups, NetworkPolicy, no public DB Cloud networking
6 Data and secrets leaked keys and passwords secrets manager, rotation, encryption at rest (KMS) Zero Trust
7 Supply chain malicious dependency or image SBOM, vulnerability scan, signed images, provenance Zero Trust

The layers are independent: the database security group does not care whether the token was valid, and the mesh policy does not care whether the WAF was bypassed. That independence is the point.


OWASP API Security Top 10 (2023)

The OWASP (Open Worldwide Application Security Project) publishes a list of the most critical risks of APIs3. It is the best checklist to bring to a design review, because almost every item is an architecture decision, not a typo. Mapped to BoxOffice:

# Risk BoxOffice example Main control
API1 Broken Object Level Authorization (BOLA) GET /orders/1042 returns order 1042 to any logged-in user every service checks that the object belongs to the caller
API2 Broken Authentication tokens without expiry; login endpoint without brute-force protection standard OIDC provider, short tokens, rate-limited login, MFA
API3 Broken Object Property Level Authorization PATCH /orders/1042 {"status":"PAID"} accepted; GET /users/me returns the password hash explicit input/output DTOs, never bind the request to the entity
API4 Unrestricted Resource Consumption one account opens 5 000 seat holds; GET /events?size=1000000 rate limits, quotas, pagination caps, hold TTL
API5 Broken Function Level Authorization a customer calls POST /admin/events role checks per endpoint, admin API on a separate route/network
API6 Unrestricted Access to Sensitive Business Flows scalper bots buy the whole stadium in seconds bot detection, per-account purchase limits, waiting room, CAPTCHA/device attestation
API7 Server-Side Request Forgery (SSRF) "import event poster from URL" fetches http://169.254.169.254/ (cloud metadata) allow-list destinations, block internal ranges, egress control
API8 Security Misconfiguration debug endpoints in production; permissive CORS; verbose stack traces hardened defaults, config review in CI
API9 Improper Inventory Management an old /v1/orders without the new checks is still online API inventory, deprecate and remove old versions
API10 Unsafe Consumption of APIs trusting the provider's webhook body blindly verify signatures, validate and re-query, timeouts

BOLA is number one for a reason

The gateway can say "this is user 42". Only the order service knows whether order 1042 belongs to user 42. Object-level authorization cannot be centralized at the edge; it lives in the code that owns the data. Test it explicitly: "user A requests user B's order β†’ 404 or 403".

Try it. Open the flash-sale scenario and press Run: watch how the burst when the sale opens (t = 60 s, our 10:00) saturates app and then inventory-db. Now select users and set its bot traffic to 0.45 β€” bots become about 30% of the requests (API6) β€” and run again. Which component would you add first, and where β€” WAF at the edge, rate limiting at the gateway, or the waiting-room queue? Use the full playground (β†—) to add the components and compare p99 and the error rate (goals count only real users).

The network & security concept map

Security is not one box on the diagram: every hop of a request answers a different trust question. The map links each palette component (green) to the concepts it enforces (blue) and to the subpage that teaches them; the blue nodes are the layers of defense in depth:

flowchart LR
    client["client Β· mobile"]:::comp -->|"login"| auth["auth"]:::comp
    client --> waf["waf"]:::comp
    waf --> apigw["apigw"]:::comp
    apigw --> mesh["mesh<br/>(order, payment, …)"]:::comp
    mesh -->|"egress via nat"| pay["payment (PSP)"]:::comp
    vpc["vpc Β· subnet Β·<br/>nat Β· vpn"]:::comp -.->|"contains"| mesh
    waf -.- c1["bots, rate limits,<br/>OWASP API4 / API6"]:::tech
    auth -.- c2["OAuth 2.1 + PKCE, OIDC,<br/>RBAC / ABAC / ReBAC"]:::tech
    apigw -.- c3["JWT validation (JWKS),<br/>strip identity headers"]:::tech
    mesh -.- c4["mTLS, SPIFFE identity,<br/>authz policy, NetworkPolicy"]:::tech
    vpc -.- c5["CIDR plan, public / private /<br/>isolated subnets, SGs"]:::tech
    pay -.- c6["secrets manager,<br/>dual-key rotation"]:::tech
    c4 -.- d1["Zero Trust: assume breach,<br/>default deny"]
    c1 -.- d2["STRIDE, OWASP API Top 10<br/>(this overview)"]
    classDef comp fill:#1e9e5a22,stroke:#1e9e5a
    classDef tech fill:#2f6fed22,stroke:#2f6fed
Palette component Question it answers Concepts Taught in Scenarios
waf Is this request an attack or a bot? OWASP API4/API6, bot rules, rate limits, waiting room OWASP API Top 10, Edge β€” security flash-sale, rate-limiter
auth Who is the user, and which client asks? OAuth 2.1, Authorization Code + PKCE, OIDC, Client Credentials, Token Exchange Identity flash-sale (extend it), blank
apigw Does the request carry a valid token? JWT validation (JWKS, iss, aud, exp), header stripping Identity β€” gateway rate-limiter, flash-sale
vpc Β· subnet Where does each component live, and what can reach it? CIDR plan, public/private/isolated subnets, route tables, SGs/NACLs Cloud networking multi-region, scale-out
nat How do private services call out? NAT per AZ, stable egress IP, VPC endpoints Cloud networking payment-system
vpn How do other networks and people get in? peering, transit, PrivateLink, VPN, session manager Cloud networking multi-region
mesh Which service may call which? mTLS, SPIFFE, authorization policy, traffic policy, NetworkPolicy Service mesh cascading-failure, retry-storm
payment Who holds the key that moves money? secrets manager, dual-key rotation, workload identity, webhook HMAC Zero Trust β€” secrets payment-system
(all of them) Can we trust the code that runs? Zero Trust, SBOM, scanning, signing, SLSA Zero Trust β€” supply chain ai-assistant

In the interview

You will rarely be asked "do threat modeling". But a strong candidate volunteers security in one or two sentences at the right time: "the database lives in an isolated subnet with no Internet route; services talk over mTLS; the mobile app uses Authorization Code with PKCE; the payment key is in a secrets manager and rotated; and at the edge, WAF plus per-account rate limits protect the on-sale flow from bots." Then move on β€” unless the interviewer digs in. Mention BOLA whenever you design an endpoint with an id in the path.

Best practices

  • Draw the trust boundaries on the architecture diagram and record each STRIDE decision with an owner β€” mitigate, accept or transfer, in the design doc, so a reviewer can see which threats were left open on purpose.
  • Deny by default β€” a route is protected unless it is explicitly listed as open, so a new endpoint cannot ship unprotected. In BoxOffice: the gateway's SecurityConfig ends with .anyExchange().authenticated() (gateway box).
  • Write negative authorization tests for every endpoint that takes an id β€” "user A requests user B's order β†’ 404" runs in CI; BOLA is API1 because nobody tests it.
  • Rate-limit per account and per IP on expensive endpoints β€” login is slow on purpose (BCrypt, ~10–20 checks/s per CPU in lab 4) and holds are scarce; bots (API6) must hit a limit before they hit the database.
  • Log security events to an append-only store and alert on them β€” failed logins, 401/403 bursts, admin actions and denied service-to-service calls, kept long enough to investigate (e.g. 1 year).
  • Patch on a clock enforced by the pipeline β€” a fixable critical vulnerability blocks the release. In BoxOffice: the Trivy gate of lab 11.

Common pitfalls

  • Security only at the gateway β€” any service reachable without passing through it (a NodePort, a debug port, a second Ingress) bypasses everything.
  • Trusting the internal network β€” "it's inside the VPC, so it's safe" is how one compromised pod becomes a full breach.
  • Rolling your own crypto, token format or login protocol β€” use standards and vetted libraries.
  • Threat model once, never again β€” the model is stale the day a new flow (webhooks, an AI assistant with tools) is added.

Key takeaways

  • Security is a design property: decide boundaries, identities and privileges while drawing the architecture.
  • Threat modeling = draw the data flow, mark the trust boundaries, run STRIDE per element, decide and record.
  • Defense in depth stacks independent layers: edge, identity, request authorization, service-to-service, network, secrets, supply chain.
  • The OWASP API Top 10 is a design-review checklist; BOLA is number one and can only be fixed in the service that owns the data.
  • In an interview, mention security briefly and concretely at the right moment β€” private subnets, mTLS, PKCE, secrets manager, rate limits.

What breaks next

Every caller in this class is a person or a service we wrote: the fan with a token, order with a workload identity, the pipeline with a signed provenance. The next caller is neither. Fans want to ask "two seats under R$ 300 on Saturday, hold them", and an assistant answers by calling catalog and inventory for them: a slow, expensive, non-deterministic dependency that can act on the inventory, on the fan's behalf, and can be talked into doing the wrong thing by the text it reads. Designing a system around a model β€” and deciding what it may touch β€” is class 13 β€” AI Systems.



  1. KOHNFELDER, L.; GARG, P. The Threats to Our Products. Microsoft, 1999 (republished by Adam Shostack on the Microsoft Security blog, 2009) β€” origin of STRIDE. See also Microsoft's Threat Modeling Tool β€” STRIDE. ↩↩

  2. SHOSTACK, A. Threat Modeling: Designing for Security. Wiley, 2014. See also the Threat Modeling Manifesto. ↩↩

  3. OWASP. API Security Top 10 β€” 2023. ↩