When threat-modeling a multi-tenant API, where does the trust boundary sit and which STRIDE categories dominate?
answer
- the perimeter is not the boundary
- the crossing repeats per endpoint
- whose data can this handler reach
- disclosure and elevation dominate
basics
~10 sIn a multi-tenant API the deciding boundary is not the network edge but every endpoint, where an authenticated caller's tenant claim meets shared tenant-owned data. Information disclosure and elevation of privilege dominate the result.
solid answer
~50 sThe edge crossing is where a stranger becomes an authenticated caller, but authentication is not isolation: once inside, every tenant's data sits on the same side of that line. The boundary that decides the threats is redrawn at each endpoint, where a caller's tenant claim meets shared data. So I list the endpoints and annotate each with what tenant-owned data it can reach, where its tenant identity comes from, and where the scoping happens. On a business-to-business payroll API with a `/reports` endpoint that takes an `employer_id` parameter, two query paths derive the employer from the verified credential and one takes it from the parameter — that third path is the finding. Information disclosure and elevation of privilege dominate, with cross-tenant writes as tampering right behind them; spoofing and denial of service live mostly at the edge.
go deeper
Be ready to say that authenticating a caller only establishes who they are, and that deciding which records they may reach is a separate check made per request.
Explain why the decisive boundary repeats at every endpoint rather than sitting at the perimeter, and map cross-tenant reads to information disclosure and acting with another tenant's authority to elevation of privilege.
Show the working method on a real design: enumerate endpoints, annotate each with reachable data and the source of the tenant identity, and point at the one query path that scopes from a request parameter.
Own the argument that per-endpoint enforcement is a design liability at scale, and that moving the check to one layer below all callers converts a review of every handler into a review of that layer's exemptions.
## What you are actually being handed A multi-tenant API service is one deployed process (or fleet) that serves many customer organizations out of shared infrastructure. Every request arrives with some credential, and behind the service sits data that belongs to a specific customer. The interview exercise is to draw that system and say what could go wrong. ## The naive drawing, and why it fails Most candidates draw one box for the internet, one box for the API, one cylinder for the data, and one dashed line between the internet and the API. That dashed line is a real trust boundary — it is where an unauthenticated stranger becomes an authenticated caller — but it is the *authentication* boundary, and authentication is not isolation. Once the caller is inside, every tenant's data is on the same side of that line. Nothing about crossing it says which rows this caller may touch. ## Where the decisive boundary really sits In a multi-tenant API the boundary that decides the threat model is **redrawn at every endpoint**. Each handler is a place where an authenticated caller's tenant claim meets tenant-owned data, and each handler either scopes the query to the caller's tenant or it does not. That makes the per-endpoint authorization check the unit of analysis: the model is not "is there a boundary?" but "how many crossings are there, and which ones are guarded?" Concretely, take a business-to-business payroll and benefits API. It exposes employee lookups, payslip downloads, benefit-election writes and a `/reports` endpoint that takes an `employer_id` parameter. The DFD looks like this in plain text: [HR admin of Employer A] --(credential + request)--> || edge || --> [Payroll API] [Payroll API] --(query)--> ( employee + payroll store, all employers ) The `||` marks the authentication crossing. But the interesting annotation is on the *second* arrow, repeated once per endpoint: for this handler, what scopes the query, and where does the scoping value come from? On three query paths that reach the payroll store, two derive the employer from the verified credential and one takes it from the `employer_id` parameter. That third path is the finding, and you can point at it on the diagram. ## Which STRIDE categories dominate, and why STRIDE names six threat categories, each the violation of one security property: | Category | Property violated | In a multi-tenant API | |---|---|---| | Spoofing | Authentication | Concentrated at the edge crossing and at internal hops that trust a header | | Tampering | Integrity | Cross-tenant *writes*: editing another employer's benefit elections | | Repudiation | Non-repudiation | One admin acts for a whole organization; weak attribution hurts disputes | | Information disclosure | Confidentiality | Cross-tenant *reads*: another employer's employee personal data | | Denial of service | Availability | A noisy tenant degrading shared capacity | | Elevation of privilege | Authorization | Acting with another tenant's authority, or beyond your role inside your own | Information disclosure and elevation of privilege dominate, because the shared datastore means a single unscoped query is a breach of another paying customer rather than a bug. Tampering follows them for the same reason on the write side. Spoofing and denial of service are real but sit mostly at the edge; repudiation matters more than people expect when one credential represents an entire organization. That ranking is the point of the exercise. A model that lists all six letters with equal weight for every endpoint has enumerated without prioritizing, and an interviewer reads it as mechanical. ## The annotation that turns a diagram into findings For each endpoint, record three things: 1. **Reachable resource** — which tenant-owned data can this handler read or write at all? 2. **Source of the tenant identity** — a verified credential, or a value the caller supplied? 3. **Where the check happens** — in this handler, in shared middleware, or nowhere? Any endpoint whose tenant identity comes from caller-supplied data, and any endpoint with no entry in column three, is a threat you can state precisely: "an authenticated HR admin at Employer A can read Employer B's employee records through `/reports`." That is a threat, not a vulnerability and not a risk — the vulnerability is the missing filter on that specific path, and the risk is the rated consequence of another employer's personal data being exposed to a customer. ## Two traps worth naming out loud **Unguessable identifiers are not a boundary.** If the model's answer to a missing check is "the ids are random", the threat has been obscured, not removed: identifiers leak through exports, support tickets, integrations and referrals, and the check is still absent when one does. **Enforcement in shared middleware moves the check but not the boundary.** It is a much better design, and it shrinks the number of places to get right, but the model now has to enumerate what bypasses it: endpoints on an exemption list, background code paths that never pass through the request pipeline, and direct datastore access from tooling. The crossings still exist; you have just made them all run through one gate, and the gate's exceptions become the interesting part. ## What a strong answer sounds like "The perimeter is where authentication happens; the boundary that decides my threats repeats at every endpoint where a tenant claim meets shared data. I would list the endpoints, annotate each with what it can reach and what scopes it, and expect information disclosure and elevation of privilege to dominate — with the writes rated alongside the reads."
- Rather than listing STRIDE letters per endpoint, what would you actually write next to each one?Three annotations: which tenant-owned data the handler can reach at all, where its tenant identity comes from (a verified credential or a caller-supplied value), and where the scoping is enforced. Any endpoint whose identity comes from caller-supplied data, or that has no enforcement entry, is a stated threat rather than a category label.
- The design says shared middleware enforces tenant scope on every request. Does that change how you draw the boundary?It does not remove the crossings, it funnels them. That is a much better design, but the model now has to enumerate what bypasses the funnel: routes on an exemption list, background paths that never traverse the request pipeline, and direct datastore access from tooling. The exceptions become the interesting part of the analysis.
- If authentication happens once at the edge, where does spoofing still belong in this model?At the edge crossing itself, where a credential is presented and could be stolen or forged, and at any internal hop that trusts an identity header the edge set. If a downstream service accepts a tenant header without re-deriving it from something verified, spoofing reappears inside a boundary people assume is safe.
A hotel keycard gets you past the lobby door once. The lock that decides whether you end up in someone else's room is on every room door, and a multi-tenant API's endpoints are those room doors.
saying these in an interview costs you the question
- Calls the network or VPC edge the tenant trust boundary
- Treats gateway authentication as tenant isolation
- Assigns all six STRIDE letters equally to every endpoint
- Claims unguessable tenant identifiers remove the threat
- Models only read endpoints and ignores cross-tenant writes