API Security — A High-Level Recap
The checklist: TLS, validate, AuthN, AuthZ, rate limit, audit. The mnemonic an API designer carries into every review.
Summary#
API security is not a single feature you bolt on at the end; it is a six-item checklist that runs on every endpoint, every release, every review. The items are independent — each one defends against a different class of failure — and skipping any one of them creates a vulnerability the others cannot cover.
The checklist, in the order an attacker tests them:
- TLS everywhere — every byte in transit is encrypted, every certificate is valid, every connection rejects downgrade.
- Validate every input — type, shape, range, format. Allowlist, not denylist. At the boundary, not deep inside.
- Authenticate every request — know who the caller claims to be, and verify they are who they say.
- Authorise per resource — given an authenticated caller, decide whether they may perform this specific operation on this specific object.
- Rate-limit per principal — cap the cost any one caller can impose. Throttle abuse before it becomes an incident.
- Audit every mutation — write a tamper-evident log of who did what and when, so the next incident can be reconstructed.
The mnemonic an API designer carries into every review is TVAARA — TLS, Validate, AuthN, AuthZ, Rate-limit, Audit. Reading it left to right matches the attacker’s path through the system: defeat TLS, slip past validation, forge identity, escalate authorisation, exhaust capacity, hide tracks.
Why it matters#
Three concrete reasons the checklist exists as a fixed sequence rather than a pile of independent practices:
- Each item defends against a different threat model. TLS stops an on-path attacker. Validation stops injection. AuthN stops impersonation. AuthZ stops privilege escalation. Rate-limit stops denial-of-service and brute force. Audit stops “we don’t know what happened.” Missing any one is a complete category of attack you cannot recover from.
- The checklist matches the OWASP API Security Top 10. Eight of the ten items are direct failures of one of the six categories. The ninth (improper asset management) is a meta-failure — endpoints exist that nobody applied the checklist to. The tenth (unsafe consumption of APIs) is the same checklist viewed from the client side.
- It is a review tool, not a runtime tool. The point is not to write code that does six things; the point is to read a code review and ask six questions. If any answer is “no” or “I don’t know”, the review fails. The cost of asking six questions in code review is trivial; the cost of finding the answer was “no” after a breach is unbounded.
For an interview, walking the checklist explicitly when the security question lands signals senior-level thinking. The candidate who lists TLS, AuthN, AuthZ, and audit in the same breath outperforms the one who launches into JWT mechanics without a frame.
How it works#
Each item in the checklist has a small set of standard implementations and a small set of recurring bugs.
1. TLS everywhere#
Every API endpoint uses HTTPS. Internal mesh calls use mTLS. Certificates are managed by an automated rotation system (cert-manager, AWS ACM, Let’s Encrypt). Old TLS versions (1.0, 1.1) are disabled at the load balancer. HSTS is set on public endpoints. Redirects from HTTP to HTTPS exist for compatibility; the canonical traffic is HTTPS.
Recurring bug: an internal admin endpoint left on HTTP because “it’s behind a VPC”, then a credential leaks because someone tcpdump’d the wire. TLS is universal, not conditional.
2. Validate every input#
Every public endpoint validates its request body, query parameters, path parameters, and security-relevant headers against a typed schema. The validator rejects unknown fields, enforces type and range, and produces a typed object the rest of the handler trusts. Below the boundary, all database access is parameterised; all subprocess calls use argument lists; all outbound HTTP escapes URL components.
Recurring bug: a mass-assignment vulnerability where PATCH /users/me blindly merges the request body into the user record, allowing is_admin: true to be set. The fix is strict schemas that reject unknown fields.
3. Authenticate every request#
Every endpoint that touches non-public data requires authentication. The mechanism is one of OAuth 2 + OIDC (browser/mobile), API keys + HMAC signing (server-to-server), mTLS (internal mesh), or SAML (enterprise SSO). The authenticator runs as middleware; it produces a typed Principal object the handler operates on. The token is verified — signature, audience, expiry, issuer.
Recurring bug: treating an OAuth 2 access token as proof of identity. The access token says “this bearer can perform these operations”; the identity lives in the OIDC id_token. Conflating them is the most common API-security CVE shape.
4. Authorise per resource#
After authentication, every operation runs through an authorisation check. The check has two questions: can this principal perform this operation? (function-level authorisation: is this caller allowed to call DELETE /users/... at all?) and can this principal access this specific object? (object-level authorisation: is the user being deleted owned by the caller’s tenant?). Both checks must run, and both must run on every request, not just at session start.
Recurring bug: IDOR (Insecure Direct Object Reference) — the handler accepts a user_id from the URL and trusts that the caller may access it because they were authenticated. The fix is to scope every query by the authenticated principal: WHERE user_id = ? AND tenant_id = ?.
5. Rate-limit per principal#
Every public endpoint has a rate limit. The limit is per principal (API key, user, IP) and per operation class (reads vs writes vs admin). The limit is enforced at the edge before reaching the handler. Limits return 429 Too Many Requests with a Retry-After header. Brute-force-sensitive endpoints (login, password reset, OTP submission) have stricter limits.
Recurring bug: a global rate limit that caps total QPS but does not isolate noisy neighbours. The fix is per-key limits, not just global ones — a token-bucket per principal.
6. Audit every mutation#
Every state-changing operation writes an audit record: who, what, when, before, after, request ID. The audit log is append-only and stored independently of the operational database (often Kafka → object storage → BigQuery). High-value mutations (privilege grants, financial transactions, deletions) trigger alerts. The audit trail is what makes the post-incident timeline possible.
Recurring bug: audit logs that include the request body but not the resolved principal, or vice versa. Either gap makes reconstruction impossible. A useful test: pick a random mutation in last week’s log and ask “could a stranger reconstruct what happened from this entry alone?”
Variants and trade-offs#
The checklist is universal, but the depth of each item varies by context.
Public partner API (Stripe, Twilio). All six items at maximum depth. TLS 1.3 only. Schema-validated every field. OAuth 2 + scopes mandatory. Per-key rate limits documented in the SDK. Immutable audit logs for compliance (PCI, SOC 2). Every breaking change goes through a deprecation cycle.
Internal microservice mesh. All six items present but tuned for latency. mTLS instead of OAuth (faster verification). Protobuf validation instead of Pydantic. Per-service quotas instead of per-key limits. Audit logs sampled at high QPS. The discipline is the same; the implementation is faster.
A weighted version of the checklist by data sensitivity:
| Data class | TLS | Validate | AuthN | AuthZ | Rate-limit | Audit |
|---|---|---|---|---|---|---|
| Public read (weather, geocoding) | TLS 1.3 | Schema | Optional API key | None | Per-IP | Sampled |
| User-scoped read | TLS 1.3 | Schema | Mandatory | Per-resource | Per-key | Sampled |
| User-scoped write | TLS 1.3 | Schema + idempotency | Mandatory | Per-resource | Per-key + per-action | Every event |
| Admin / financial | TLS 1.3 + mTLS | Schema + business rules | MFA-elevated | Per-resource + 4-eyes | Strict | Every event + alert |
The checklist does not say “skip items for less sensitive data”. It says “tune the depth per category”. Even a public weather API needs TLS, validation, and rate limiting — the items where it costs almost nothing to apply at full strength.
When this is asked in interviews#
The security question lands in three shapes; the checklist handles all three.
- “How would you secure this API?” — Walk the six items in order, naming one concrete mechanism for each. TLS via ACM. Validation via Pydantic / Zod. AuthN via OAuth 2 with OIDC. AuthZ via row-level scoping. Rate limiting via a token bucket at the edge. Audit via a Kafka topic with retention. Sixty seconds, full coverage, senior signal.
- “What is the most common API security bug?” — Two strong answers: IDOR (object-level authorisation gap, OWASP API1) or conflating AuthN and AuthZ (treating an OAuth access token as identity). Either answer leads naturally into the checklist.
- “How do you review a new endpoint for security?” — The checklist as a review tool. Six questions, written down, asked on every PR. “Show me where this endpoint validates inputs. Show me where it authorises the principal against the resource. Show me the rate-limit configuration. Show me the audit entry it produces.”
The strongest summary to leave the interviewer with: “Security is a six-item checklist applied on every endpoint, every release. Missing any one is a different shape of failure I cannot recover from.” That sentence is the difference between an API designer and a feature engineer.
Related concepts#
- API Security — An Overview — the broader frame: threat model, attacker capabilities, defence in depth.
- Transport Layer Security (TLS) — item 1 in detail: TLS protocol, certificates, mTLS.
- Authentication vs Authorization — items 3 and 4 in detail: AuthN vs AuthZ, the vocabulary precision.
- OAuth 2 — The Authorization Framework — the standard AuthN/AuthZ mechanism for modern APIs.
- Securing APIs Using Input Validation — item 2 in detail: validation patterns, OWASP API Top 10.