APIs

API security basics every SaaS team should nail first

Authentication, object-level authorisation and rate limiting — the unglamorous controls that prevent the worst breaches.

19 May 2026 · APIs

APIs now carry most of the traffic we test, and they fail differently from the web pages they replaced. A browser application hides things by not rendering them; an API has no such illusion to hide behind. Every endpoint is directly reachable, every parameter is directly editable, and the client is entirely under the attacker's control.

Four controls prevent the large majority of serious API findings. None of them are difficult. They are simply easy to leave until later.

1. Authenticate every endpoint, including the ones you forgot

The most common way authentication fails on an API is not a broken algorithm. It is an endpoint that was never wired into the middleware — a health check that grew into a diagnostics endpoint, an internal service route exposed through the same gateway, a legacy version left running after v2 shipped, a webhook receiver that trusts anything that reaches it.

Enumerate what you actually expose rather than what you believe you expose. Generate the route list from the framework, compare it against the documented API, and confirm every route rejects an unauthenticated request. Where a webhook must accept external calls, verify a signature — a shared secret in a header is not a signature and does not survive being logged.

If you issue JWTs: validate the algorithm against an allowlist rather than trusting the token's own header, verify the issuer and audience, and keep access token lifetimes short with refresh handled server-side. A token you cannot revoke is a credential you cannot take back.

2. Authorise at the object level, every time

This is the one that produces the serious breaches. Authentication answers "who are you"; object-level authorisation answers "may you have this particular record". The second question is asked far less consistently than the first.

The failure looks trivial: GET /api/v2/orders/88213 returns an order belonging to another customer, because the handler looked the order up by its primary key and checked only that the caller was logged in. It is trivial to exploit — increment the number — and trivial to script across the whole range.

The reliable fix is to make the tenant a mandatory part of every query rather than a check that follows it. Scope at the data layer so that a lookup by ID alone cannot return another tenant's row: row-level security in the database, or a repository layer that refuses an unscoped query. Checks that live in handlers get forgotten in the next handler somebody writes.

The same applies to properties, not just objects. If your update endpoint binds the whole request body to the model, a caller can set role or account_id even though the UI never offers it. Accept an explicit allowlist of fields per endpoint.

3. Rate limit, and limit the right things

Rate limiting is usually thought of as an availability control. On an API it is equally an access control: it is what turns "one record leaked" into "one record leaked" instead of "the entire customer table enumerated overnight".

Limit per authenticated identity and per IP, and apply stricter limits to the endpoints that matter: login, password reset, token issuance, search, export, and anything that accepts an identifier. Return HTTP 429 with a Retry-After header so legitimate clients back off correctly. And cap pagination — an endpoint that accepts limit=100000 is an export endpoint whether you intended one or not.

4. Say less in errors and responses

APIs leak through verbosity. A login endpoint that distinguishes "unknown user" from "wrong password" is a user enumeration tool. A stack trace in a 500 response gives away framework versions and file paths. And the quiet one: serialising the whole database record and letting the client display a subset. The client is not doing the hiding — everything you send is visible. Return only the fields the endpoint needs, defined explicitly.

Where to start

If you are looking at this list and wondering what to do on Monday: take your five highest-traffic endpoints and the five most recently shipped. For each, confirm it rejects an unauthenticated call, returns nothing for another tenant's identifier, refuses an oversized page request, and returns only intended fields. That exercise takes an afternoon and finds more than most scanners will.

Object-level authorisation is the control worth your attention first. In our engagement data it is both the most frequently missing API control and the one behind the largest data exposures we report.

Next step

Want this checked on your own systems?

We scope the work with you in writing before any testing starts.