Cloud-native, security-first rules for any API exposed on the public internet.
8 domains · 23 rules.
Authenticate at the edge before requests reach your service. Keep tokens short-lived, scoped minimally, and out of URLs. Every downstream failure in this domain is a full account compromise.
Authorization headers exclusively.All traffic must be encrypted. Every default that is too permissive is an attack surface — CORS wildcards, missing HSTS, and unvalidated content types are all exploitable.
Strict-Transport-Security: max-age=31536000; includeSubDomains so browsers never downgrade to HTTP.Access-Control-Allow-Origin: * is only safe for fully public, read-only, unauthenticated endpoints. Everything with auth requires an explicit allowlist.Limiting is not a single dial. Granularity matters — a global cap does nothing to stop a single actor hammering one expensive endpoint. Defense must happen at multiple layers.
Per-IP, per-user, per-endpoint, per-tenant. Each granularity catches a different class of abuse that the others miss.
After N consecutive failed auth attempts, introduce forced delays or lockouts. This stops credential-stuffing without blocking legitimate users.
Application-layer limiting is too late if volumetric floods hit your origin. Cloudflare, AWS Shield, or equivalent must absorb attacks before your code runs.
Invalid input must be rejected at the edge of your service, not propagated inward. Output discipline is equally important — what you return becomes another party's attack surface.
JSON Schema, Zod, Pydantic — pick the stack equivalent. Reject with a 400 immediately; never let malformed data reach your domain logic.
Even JSON APIs can carry stored XSS if responses are rendered. Encode output; don't assume consumers are safe.
Unbounded queries are both a DoS vector and a data-exfiltration path. Server-side page size ceiling — never caller-controlled without a hard limit.
Return only what the contract defines. Serializers that auto-expose model fields — password_hash, internal IDs — are a common accidental leak.
API design decisions made early become hard to reverse once clients depend on them. Three choices — versioning, ID scheme, and error shape — have outsized security implications.
{"error":{"code":"...","message":"..."}} lets clients handle errors programmatically. Never expose stack traces or internal paths.Secrets committed to code or stored in plaintext env vars are already exposed — it is only a matter of when they are found. Separation of config from code is both a security and operational requirement.
Logs and metrics are not just operational tooling — they are your only way to detect an active attack. What you cannot see, you cannot defend. Build this in from day one.
Authorization tokens, passwords, or PII — logs are a secondary breach surface.The network perimeter is part of the API's security surface. A well-secured application running on a misconfigured host is still vulnerable. Minimal exposure is a first-class requirement.
If you are behind a CDN or proxy, your origin IP must be firewalled to accept traffic only from the CDN's IP ranges. An exposed origin bypasses all edge protections.
Internal services stay on private subnets. Only the API gateway or load balancer faces the internet — everything behind it is unreachable directly.
Manual cert renewal is an operational risk with a predictable failure mode. ACM, Let's Encrypt with certbot, or CDN-managed certs with auto-renewal eliminate expiry incidents.
npm audit, pip-audit, trivy — whichever fits the stack. Known CVEs in dependencies are the most common attack vector for otherwise well-designed APIs.
The edge authenticates.
The gateway rate-limits.
The service validates.
The data layer least-privileges.
Observability catches what everything else missed.