PXL Security LTD, Sofia, Bulgaria Offensive security since 2014[email protected]
APIWeb SecurityOWASP

The OWASP API Security Top 10, in practice

By PXL Security28 September 202113 min read

As applications moved to API-first architectures, the bugs moved with them — but they changed shape. OWASP's API Security Top 10 captures the shift. On our engagements, a few of its categories account for the overwhelming majority of serious API findings.

Broken object-level authorization (the big one)

Known as BOLA or IDOR, this is the single most common serious API bug we report. An endpoint returns an object by ID, checks that you're authenticated, but never checks that the object is yours. Change the ID, read someone else's data. APIs make it worse because every mobile app and SPA talks to hundreds of object-scoped endpoints — hundreds of places to forget one ownership check. The fix is a principle: authorise every object access against the caller, in the service that owns the data.

Broken authentication and object-property-level authorization

APIs expose authentication directly, so weaknesses are directly reachable: tokens that don't expire, that aren't revoked on logout, that leak in URLs. And mass-assignment — where a client sets fields it shouldn't, like "role": "admin" — turns a profile update into a privilege escalation. Allow-list the fields a client may set; never bind request bodies straight onto objects.

Unrestricted resource consumption

Without pagination limits, rate limits and query-cost controls, one request can force an API to do enormous work. We routinely show a single call returning thousands of rows, or a GraphQL query fanning out into many full-table scans. Bound page sizes, cap query depth and cost, and rate-limit by principal.

The practical takeaway

You don't need to memorise all ten. If you enforce object-level authorization everywhere, handle tokens properly, allow-list writable fields, and put limits on resource use, you've closed the categories behind most of the serious API findings we see. Test them explicitly — these are exactly the issues a scanner, lacking authenticated context, tends to miss.

Shadow and forgotten APIs

You can’t secure an endpoint you’ve forgotten exists. Old API versions left running after v2 shipped, undocumented internal endpoints, debug routes, and staging hosts exposed to the internet are a recurring source of serious findings — typically because the authorization fixes landed on the current version while the old one stayed online and unpatched. Inventory every version and host that answers, retire what you don’t need, and make sure your testing scope includes the endpoints nobody remembered to mention.

Documentation as a security control

An accurate, current API specification isn’t just developer convenience — it’s a control. It defines what “valid” looks like, which makes strict schema validation possible, and it makes shadow endpoints visible precisely because they’re absent from it. Teams that treat the spec as the source of truth find it far easier to reason about what their API exposes — and far harder to leave an old version quietly answering requests.

Broken function-level authorization: not just a bigger BOLA

BOLA asks whose record you may touch; broken function-level authorization (BFLA) asks which operation you may invoke at all. The two are often confused because both end in an unauthorised action, but the failing control is different. BOLA breaks when a handler trusts an object identifier from the request. BFLA breaks when a route that should be reserved for a privileged role is reachable by anyone who can find it.

In practice BFLA hides in the gap between read and write, and between user and administrator. An API may carefully gate its listing endpoint yet forget that the sibling endpoint which deletes, exports, or re-prices the same resource sits one verb away. Because REST maps operations onto HTTP methods, a route guarded for GET is frequently wide open for PUT, PATCH, or DELETE. Predictable administrative path segments make the reserved functions easy to enumerate once the naming convention is known.

How to enforce/detect

  • Deny by default at the function level: every route asserts a required role or scope, and a request with no matching grant is rejected before any handler logic runs.
  • Drive authorization from the server's own role model, never from a role, group, or tier claim the client supplies.
  • Treat each method on a path as a distinct decision; do not assume a guard on one verb covers the others.
  • Detect by diffing effective permissions per role against the route table in CI, and by alerting when low-privilege sessions reach administrative path prefixes.

Server-side request forgery in API back-ends

APIs are unusually exposed to SSRF because fetching things on the client's behalf is a normal feature: webhook registration, link previews, document import from a URL, avatar fetching, and integrations that accept a callback address. When the server dereferences a location the caller controls, an attacker can pivot the request inward, toward cloud metadata services, internal admin panels, and databases that trust the network rather than the caller.

Modern deployments make this worse. Container and cloud environments expose sensitive endpoints on link-local and private ranges, and blind SSRF still leaks through response timing, size, and status differences even when no body is returned. Naive blocklists are routinely defeated by redirects, alternative IP encodings, and hostnames that resolve to internal addresses only at request time.

How to enforce/detect

  • Resolve the hostname, validate the resulting IP against an allowlist of permitted destinations, and connect to that validated address so a later DNS answer cannot change the target.
  • Reject private, loopback, and link-local ranges, and disable or strictly bound redirect following.
  • Route outbound fetches through an egress proxy with its own allowlist, and deny access to cloud metadata endpoints at the network layer.
  • Detect by logging the resolved destination of every server-initiated fetch and alerting on connections to internal ranges.

Unsafe consumption of third-party APIs

Most of the Top 10 assumes the attacker is your client. This one assumes the attacker is upstream of you. When your service calls a partner, payment provider, or data feed, it is easy to extend to that response the trust you would never extend to a user. A compromised or merely sloppy upstream can then feed you malicious redirects, oversized payloads, or data that flows straight into a sink.

The weak points are predictable: following upstream redirects blindly, trusting TLS without verifying it, and parsing third-party responses without the limits and validation you apply to inbound traffic.

How to enforce/detect

  • Validate and sanitise data from integrations exactly as you would data from end users, including type, size, and encoding.
  • Verify TLS certificates, apply strict timeouts, and cap response sizes to resist slow or oversized replies.
  • Do not blindly follow redirects returned by an integration, and isolate integration calls so one failing upstream cannot exhaust shared resources.

Where to enforce: gateway versus service

An API gateway is the right place for coarse, cross-cutting controls: authentication, TLS termination, global rate limits, and request shaping. It is the wrong place to make the final authorization decision, because the gateway rarely knows which object is being addressed or who owns it. Gateway-only enforcement therefore fails precisely where BOLA and BFLA live.

The durable pattern is defence in depth: the gateway filters obvious abuse and establishes identity, and the service makes the per-request, per-object decision against its own data, so an internal caller that skips the gateway is still denied.

How to enforce/detect

  • Keep object- and function-level authorization in the service, next to the data, not only at the edge.
  • Require authenticated, authorised service-to-service calls so bypassing the gateway grants nothing.
  • Harden misconfiguration everywhere: generic error bodies with no stack traces, a strict origin allowlist rather than reflected CORS origins or wildcard-with-credentials, and security headers applied consistently.
  • Detect drift by asserting these controls in integration tests that call services directly, bypassing the gateway.

How many of your endpoints check ownership?

An API-focused penetration test answers that concretely — the exact endpoints that leak, and the fix for each.

Scope an API test