DEV Community

Cover image for Threat-Model a Small API Before Your Security Interview
Karuha
Karuha

Posted on • Originally published at aceround.app

Threat-Model a Small API Before Your Security Interview

Security interview answers get vague when the candidate starts with a framework. Start with a system instead: show where trust changes, name one thing worth protecting, rank a few believable abuses, then explain the control you would ship first. This small JavaScript drill makes that reasoning visible.

You do not need to recite every STRIDE category to do this well. A good answer is usually narrower: “Here is the boundary I worry about, here is the abuse case, and here is how I would reduce it without breaking the product.”

A security architecture visual break

What does a strong threat-model answer actually contain?

In a 45-minute security interview, a whiteboard threat model is not a compliance artifact. It is evidence that you can make decisions under uncertainty.

I look for four moves:

  1. Draw the data flow. “The browser calls our API; the API calls the payment provider and writes an order.” Those arrows are more useful than a page of security nouns.
  2. Call out a trust boundary. A request crossing from a client you do not control into a service you do is a boundary. So is an inbound webhook from a vendor.
  3. Name a concrete abuse case. “An attacker changes the price in the browser” is testable. “Tampering” is not yet an answer.
  4. Choose a proportionate control. Explain what changes, what it costs, and what risk remains.

That sequence maps well to the threat-modeling guidance from OWASP and NIST, but it is also practical: it gives an interviewer places to probe. If they ask “what happens if the webhook is replayed?” you already have a model to extend.

Can you practice that loop without a giant framework?

Yes. Put a deliberately small model in code, score the abuse cases, and make yourself justify the top result. The point is not that multiplication produces security truth; it is that a visible, repeatable ranking stops you from treating every risk as equally urgent.

Save this as threat-model-drill.mjs and run it with a recent Node.js or Bun runtime:

import assert from "node:assert/strict";

const system = {
  name: "checkout-api",
  boundaries: [
    "browser → public API",
    "API → payment provider",
    "API → order database",
  ],
  assets: ["session token", "order total", "payment intent"],
  entryPoints: ["POST /checkout", "POST /webhooks/payment"],
  threats: [
    {
      id: "T1",
      abuse: "Replay a captured payment webhook",
      likelihood: 3,
      impact: 5,
      control: "Verify the provider signature and persist event IDs before processing.",
    },
    {
      id: "T2",
      abuse: "Change a client-supplied order total",
      likelihood: 4,
      impact: 4,
      control: "Recalculate prices from server-side product data.",
    },
    {
      id: "T3",
      abuse: "Submit a stolen session token",
      likelihood: 2,
      impact: 4,
      control: "Use short-lived tokens, rotation, and step-up checks for sensitive changes.",
    },
  ],
};

const score = ({ likelihood, impact }) => likelihood * impact;
const band = (value) => (value >= 15 ? "high" : value >= 8 ? "medium" : "low");

function review(model) {
  const ranked = model.threats
    .map((threat) => ({ ...threat, score: score(threat), band: band(score(threat)) }))
    .sort((a, b) => b.score - a.score);

  console.log(`\n${model.name}: ${model.boundaries.length} trust boundaries`);
  console.table(
    ranked.map(({ id, abuse, score, band, control }) => ({
      id, abuse, score, band, control,
    })),
  );
  return ranked;
}

const ranked = review(system);
assert.equal(ranked[0].id, "T2");
assert.equal(ranked[0].band, "high");
assert.equal(band(7), "low");
Enter fullscreen mode Exit fullscreen mode

The output puts the changed order total first with a score of 16, then replayed webhooks at 15. That gives you an interview-ready opening:

“I would start by not trusting the total sent by the browser. It is a high-likelihood, high-impact integrity issue, and the fix is to derive price on the server. Next I would make payment webhooks idempotent and verify their signature, because a valid event can still be replayed.”

Notice the useful detail here: signature verification and replay protection are separate controls. A signed message proves who created it; it does not, by itself, prove that you have not processed it already.

How do you make the model sound like engineering rather than a checklist?

Add the product constraint immediately after the control.

For the price example, server-side calculation means the API needs an authoritative product record and a defined rule for discounts. That is a small increase in backend complexity, but it removes a client-controlled integrity decision.

For webhook replay, an event-ID store makes the handler idempotent. You would then decide how long to retain IDs based on the provider’s retry window and your operational budget. That follow-up is much stronger than saying “I would use encryption,” because it connects a defense to a failure mode and a cost.

A concise interview answer can follow this template:

Say Why it helps
“The boundary is browser to API.” Establishes what you do not trust.
“The asset is the final order amount.” Shows what success looks like for an attacker.
“The abuse is a modified request body.” Makes the scenario falsifiable.
“The server recalculates price; residual risk is promotion-rule bugs.” States a control without pretending risk disappears.

The last clause matters. Security work is rarely “solved.” If you describe the residual risk, you show that you can make a scoped decision rather than promise a magic control.

What follow-ups should you rehearse?

Change one input at a time and say what moves in the model.

  • A marketplace adds third-party sellers. How do price authority and seller permissions change?
  • The payment provider retries for 72 hours. What does that do to event-ID retention?
  • The API becomes mobile-first. Which token storage and device-compromise assumptions are different?
  • A support tool can issue refunds. Where is the new privileged boundary, and what audit trail do you need?

Try answering each follow-up in 60 seconds. If you lose the thread, return to the four moves: system, boundary, abuse, control. You do not need a bigger vocabulary; you need a clearer chain of reasoning.

For realistic timed practice after you have written your own scenarios, aceround.app — AI interview assistant can help you rehearse the explanation out loud. Keep the model grounded in systems you understand; no tool can substitute for knowing why a control fits the risk.

FAQ

Should I use STRIDE in a security interview?

Use it if it helps you find missed abuse cases, not as the answer itself. Start with the data flow and name the two or three threats that change the engineering decision.

Is likelihood times impact a real risk model?

It is a practice aid, not a replacement for an organization’s risk process. Its value here is forcing an explicit comparison between plausible threats so you can explain your priority.

What if I do not know the “right” control?

State the assumption, choose a reversible first control, and name what you would validate next. For example: “I would rate-limit the endpoint now, then examine logs to decide whether account-level velocity controls are needed.”

References

Disclosure: AI assisted with drafting and editing. The author reviewed the technical examples, code, and sources.

Top comments (0)