Sentinelwares
All articlesApplication Security

API security: where modern applications actually leak

APIs fail differently from web applications. Authorisation, not injection, is where the serious findings concentrate — and it is the hardest thing to automate.

Michael RodriguezJanuary 2, 20259 min read

APIs now carry most of the traffic and most of the data in a typical application, and they fail in ways the traditional web security model did not anticipate. The interface is machine-readable, so enumeration is trivial. The authorisation surface is wide, because every endpoint makes its own decision. And a single predictable identifier can expose an entire dataset.

Authorisation is the main event

The OWASP API Top 10 leads with broken object level authorisation, and that ordering reflects reality. It is comfortably the most common serious finding we report.

The pattern is simple. An endpoint accepts an identifier — GET /api/v1/invoices/4821 — authenticates the caller correctly, and then returns the record without checking whether *this* caller is entitled to *that* record. Change the number, read someone else's invoice.

It is trivial to exploit and nearly invisible to automated tooling, because the scanner has no way to know that invoice 4821 belongs to a different tenant. Detecting it requires two accounts and an understanding of what the data represents.

Closely related:

Broken function level authorisation. The administrative endpoint is hidden from the user interface but not protected on the server. A low-privilege user who calls it directly succeeds.

Broken object property level authorisation. The record returned is the right one, but it includes fields the caller should not see — internal notes, cost prices, other users' contact details. Equally, an update endpoint accepts fields it should ignore, letting a user set their own role to admin. This is mass assignment, and it is common in frameworks that bind request bodies to models automatically.

The fix in every case is the same shape: authorise against the authenticated identity at the point of data access, not at the routing layer, and never rely on the client to omit what it should not send. Define explicitly which fields are readable and writable per role rather than serialising the whole model.

Enumeration and abuse

Unrestricted resource consumption. An endpoint that accepts ?limit= without an upper bound, or a GraphQL query with unbounded nesting, is a denial-of-service vector and often a bulk-extraction vector. Enforce maximums server-side.

Rate limits that are not really limits. Per-IP limiting is defeated by a residential proxy pool. Limit per authenticated identity, and apply stricter limits to sensitive operations: authentication, password reset, search, export.

Enumerable identifiers. Sequential integers tell an attacker exactly how many records exist and how to iterate them. This is not a substitute for authorisation, but unpredictable identifiers meaningfully raise the cost of large-scale extraction.

The endpoints you forgot

Undocumented and deprecated versions. /api/v1/ is still running months after /api/v2/ shipped, without the authorisation fixes that went into v2. We find this constantly. Inventory every deployed version and decommission deliberately.

Debug and internal endpoints. Health checks that dump configuration, GraphQL introspection left enabled in production, framework debug routes. Each leaks information that shortens an attacker's reconnaissance considerably.

Endpoints missing from the specification. If you generate documentation from code, this is less of a problem. If documentation is maintained separately, it is always incomplete — which is why discovery matters independently of what your spec says.

Getting the basics right

Validate input against a strict schema and reject anything that does not conform, rather than attempting to sanitise. Enforce TLS everywhere with modern configuration. Keep authentication tokens short-lived, validate signature and audience properly, and provide a revocation path. Log authorisation failures — a spike in them is one of the clearest attack signals you will get.

Testing that finds these

Automated scanning catches injection, misconfiguration, and known component vulnerabilities. It will not catch authorisation flaws, because those require knowing what the data means and who should be allowed to see it.

Effective API testing means creating accounts in each role and each tenant, then systematically attempting to access every object and function from every role. It is methodical work and it is where the serious findings come from.

If your API testing has never produced an authorisation finding, that is not usually evidence that none exist. It is more often evidence that nobody has looked properly.

API SecurityDevSecOpsOWASP API Top 10