APIs are where modern applications meet the internet, which makes them the primary attack surface. Securing an API is not one big feature but a set of disciplines applied consistently to every endpoint. This is the checklist we run before any API we build goes live, and it complements the category-by-category fixes in our OWASP Top 10 guide.
Authentication: prove who is calling
Every non-public endpoint must verify identity. Use a well-tested standard rather than rolling your own, keep tokens short-lived, and handle refresh carefully. Tokens are also where many teams go wrong in subtle ways, which is why we wrote a dedicated piece on JWT mistakes that get applications breached.
Authorisation: check what they can do
Authentication is not authorisation. After you know who the caller is, you must check whether they are allowed to perform this specific action on this specific resource. Enforce it on the server for every request, check ownership of records, and deny by default. Broken authorisation is consistently the most damaging API flaw.
Validate every input
Treat all incoming data as hostile. Validate types, ranges, and formats against a strict schema and reject anything that does not conform. This single discipline closes off injection, malformed-data crashes, and a long tail of abuse. Validation belongs at the boundary, before the data touches any business logic.
Rate limit and throttle
Without rate limiting, a single client can overwhelm your service or brute-force credentials. Apply sensible limits per client and stricter limits on sensitive operations like login. A shared, atomic store makes this reliable across instances, which is one of the production Redis patterns we rely on.
Do not leak information
Error responses should help legitimate developers without handing attackers a map. Keep production errors generic, never return stack traces or internal identifiers, and be careful that timing and response differences do not reveal whether an account exists. Verbose errors are a quiet but real vulnerability.
Transport and headers
Serve everything over TLS, with no plaintext fallback, and set the security headers that instruct browsers to behave safely. Configure cross-origin policy explicitly rather than allowing everything. These are low-effort, high-value controls that should be defaults on every service.
Test it like an attacker would
A checklist applied at design time is necessary but not sufficient, because the gap between intended behaviour and actual behaviour is where breaches live. The complement is to probe your own API the way an attacker would: try to access another user's records by changing an id, send malformed and oversized payloads, hammer an endpoint to see whether rate limiting actually engages, and call privileged operations with an under-privileged token. Much of this can be automated and run continuously, so a regression in an authorisation check is caught the same way a failing unit test would be. Security that is only verified by reading the code on the day it was written decays as the code changes around it. Treat these abuse cases as first-class tests that run on every change, and the checklist becomes a guarantee rather than an intention.
Log, monitor, and assume breach
Record security-relevant events so you can detect and investigate abuse, and alert on the patterns that indicate an attack in progress. Combined with a zero trust posture, this is what turns a breach from a silent catastrophe into a contained incident. If you want this checklist enforced on your APIs by specialists, our security and QA service does exactly that.