Level: intermediate. You are assumed to know HTTP, JSON and how a REST endpoint gets called from a script or a mobile app. This walks the OWASP top ten in order: what each weakness looks like, why it happens, and the control that closes it. New to the subject? Read how an attack chain unfolds first.
API security is the practice of protecting the programmatic interfaces software uses to talk to other software. Most web front ends, mobile apps and payment integrations are, underneath, clients of an API. That makes the API layer the part of a system with the least human oversight: no address bar, no login form, and often no prompt at all before a request runs. A weakness there is invisible to a user and to a network scanner alike, so if your team only tests what it can click through in a browser, the most exposed surface is the part nobody looked at. OWASP maintains a dedicated API security project for this reason.
What API Security Actually Covers
API security has four overlapping jobs. Authentication: proving who is calling. Authorisation: proving they may touch this object, property or function. Input handling: making sure supplied data cannot change the meaning of what the server does. Exposure control: making sure responses and logs reveal no more than the caller should see.
Three of those four transfer directly from web security. The one that does not is discovery. A website is linked and walked. An API is a private surface that exists where a developer typed a URL, and forgotten endpoints accumulate quietly. OWASP reflects this in the OWASP API Security Top 10 for 2023, where inventory management earns its own entry.
The OWASP Top 10 at a Glance
These are the ten weaknesses, in OWASP’s order, with the security cause and the control for each. The grouping below collapses them into three root causes.
| ID | Weakness | How it breaks | Primary control |
|---|---|---|---|
| API1 | Broken object level authorisation | An object ID in the URL is swapped for another user’s | Check ownership on every request |
| API2 | Broken authentication | Sessions, tokens or keys accepted too loosely | Strict token validation, short lifetimes |
| API3 | Broken object property level authorisation | A caller can edit fields they should only read | Allow-list writable fields |
| API4 | Unrestricted resource consumption | One call consumes far more than intended | Rate limits, caps, timeouts |
| API5 | Broken function level authorisation | Admin-only routes answer ordinary callers | Enforce roles server-side |
| API6 | Unrestricted access to sensitive business flows | Steps automated faster than a human can | Step limits, monitoring |
| API7 | Server side request forgery | The server fetches a URL the caller chose | Allow-list destinations |
| API8 | Security misconfiguration | Debug endpoints or verbose errors left on | Harden by default, verify in CI |
| API9 | Improper inventory management | Old versions and shadow endpoints still answer | Automated discovery, decommissioning |
| API10 | Unsafe consumption of APIs | Upstream data trusted without validation | Validate and constrain responses |
The Ten Grouped by Root Cause
Ten items sound like ten fixes. It is really three causes: authorisation not enforced, input not constrained, and trust assumed.
Broken object level authorisation is the most common serious flaw, and it has a simple shape. A user requests /orders/1042 and receives order 1042, because the check that ran was “is the caller logged in” rather than “does 1042 belong to this caller”. Change the number in the URL and the server returns someone else’s data. The fix must be repeated everywhere: fetch the object, confirm the subject may access it, then serialise. One shared helper beats fifty hand-written checks, because the fiftieth gets forgotten. The API1 entry is worth reading in full.
Broken object property level authorisation is the same family one level down. A caller may read their profile, but the update endpoint also accepts a role, balance or verified flag, and the server mass-assigns those from the request body. The remedy is an explicit allow-list of updatable fields per endpoint rather than binding the whole body to the model. API5 covers the sibling case: administrative routes that exist in the code but check the role only in the client, or not at all.
Identity, data and flow failures: API2, API4 and API6
Broken authentication is the API equivalent of leaving the key in the door: accepting an unverified token, honouring a token whose audience is a different service, skipping the expiry check, or failing to bind a session to the client that opened it. Since APIs have no login form, a weaker path often survives because nobody looks. Treat token validation as a library you trust, keep lifetimes short, and keep keys out of mobile binaries.
Unrestricted resource consumption is the availability half. An endpoint accepting a page size of 100000, or an upload with no maximum, lets one caller turn your database into their personal fan-out. The controls are caps: bounded page sizes, bounded bodies, timeouts, concurrency limits, and rejection rather than silent truncation. Rate limit on cost rather than request count.
Unrestricted access to sensitive business flows is the subtle one. Each call is individually valid, and the attack is the volume: automating a checkout, voucher claim or ticket purchase faster than a human could, and holding the stock it removes. OWASP notes that some flows are worth more when slower. Rate limit the flow rather than the endpoint, and alert on patterns.
Trust-boundary failures: API7, API8, API9 and API10
Server side request forgery appears wherever the server fetches a URL, hostname or path supplied by the caller: an avatar loaded from a URL, a webhook registered with an address, an import from a link. Impact depends on where the server can reach, so control it with an allow-list of destinations plus a block on private and link-local ranges, applied after DNS resolution to defeat rebinding. Never let a caller choose the protocol either.
Security misconfiguration in APIs is rarely exotic: verbose stack traces, a live /debug route, permissive CORS, a session that still works after deactivation, a staging copy still routed in production. It persists because hardening happens by hand at deploy time. Make it a build-time check that fails the pipeline when the shipped configuration differs from the reviewed baseline.
Improper inventory management is the forgetfulness problem. API1 to API3 are hard to find by hand, so the answer is automated discovery. Publish an OpenAPI schema, scan against it on a schedule, and give every endpoint an owner. Old versions are the usual culprit when /v1/ and /v2/ both answer.
Unsafe consumption of APIs is the item most teams skip. When your backend calls a partner, the response arrives from outside your control, and permissive type coercion there can undo careful validation on the way in. Validate every upstream response against a schema, constrain types, and fail closed when validation fails.
There is a clear legal line. You may test APIs you built, or ones you have written permission to test, with the permission recorded in advance. You may not probe a third party’s API, and “it was only a GET request” is no defence under the UK Computer Misuse Act 1990 or the Nigerian Cybercrimes Act 2015. When unsure, ask in writing.
Within that permission the method is straightforward. Start with the schema, not the server: list every path, method, parameter and the authentication each expects. That list is your attack surface, and any endpoint missing from it is a finding before testing begins. The PortSwigger API testing material explains the shape of these tests.
Then work the list mechanically and record evidence. Authenticate as two low-privilege accounts, then replay each account’s request with the other’s identifier, across every endpoint taking an object ID. That single test covers most of API1; repeat it across role pairs for the function-level version. For write endpoints, submit an extra field a normal user should not control. Send oversized page sizes and bodies. Check that one user’s response never contains another user’s data.
Four habits separate useful testing from noise. Change one variable at a time, so you know what caused the difference. Keep raw requests and responses, because fixes are retested against the same evidence. Stop once impact is proven.
Prioritising the Security Fixes
Ten findings rarely get fixed in list order. Authorisation comes first, because any authenticated user can exploit those and they are the most likely to be a real breach. Consumption and input limits next, protecting availability and cost. Misconfiguration, inventory and unsafe consumption after that, as hygiene that prevents recurrence.
Then fix the security class, not the endpoint. One corrected route leaves three siblings vulnerable, so treat each category as a design change: one authorisation helper, one schema validator, one fetch guard.
Frequently Asked Questions
What is the most common API vulnerability?
Broken object level authorisation, listed as API1, which OWASP has placed at the top of the list for consecutive editions. It is common because the fix is easy to omit on one new endpoint, and easy to skip entirely when a developer copies a pattern that only checked authentication.
How do I find undocumented API endpoints?
Publish an accurate schema, then compare it with what the server actually routes: scan the deployed host from an authorised position, and check gateway logs for paths requested but absent from the schema. Anything serving traffic without an owner is the finding.
No. A WAF is useful for rate limiting and known bad patterns, and makes a reasonable security backstop, but it cannot know whether object 1042 belongs to the caller. That decision belongs in application code.
How often should APIs be security tested?
Continuously enough to catch regressions before release, and thoroughly by a person around each significant change. Automated schema and authorisation tests belong in continuous integration, where they run on every commit.
Key Takeaways
- APIs are the least visible and often the most exposed part of a modern system.
- Broken object level authorisation is the most common serious flaw, and the fix is an ownership check on every request.
- Authorisation belongs in application code. A WAF is a backstop, not a substitute.
- Consumption limits protect availability and cloud cost, and belong in code as well as at the edge.
- Discovery and decommissioning are security controls, not paperwork.
- Treat every upstream API response as untrusted input and validate it against a schema.
- Test only what you own or have written permission to test, and keep the evidence.
For the human side of the same problem, see how phishing still gets credentials past every control once an API is properly locked down.
Sources: OWASP API Security Top 10 (2023); OWASP API Security project; PortSwigger Web Security Academy, API testing; OWASP API1, Broken object level authorisation.
0 Comments