API Security: Defending Against the OWASP API Top 10
Picture this. Your customer-facing API returns full account details for any account number submitted by any authenticated user. No ownership check. Just authentication. A security researcher writes a 12-line Python script that iterates account IDs from 1 to 500,000 and downloads every customer’s balance, transaction history, and personal details in under three hours. The endpoint has been live for years. It passed multiple penetration tests. No automated scanner flagged it because the requests were all properly authenticated with valid JWTs.
That is BOLA (Broken Object Level Authorization), and it is the number one API vulnerability for a brutally simple reason: detecting it requires business context that no automated tool possesses. A WAF sees a valid token, a valid endpoint, and a valid response format. Everything looks correct. The business logic failure, that user A should not be able to read user B’s invoice, is completely invisible at the network layer.
The OWASP API Security Top 10 exists as a separate list from the Web Application Security Top 10 because API vulnerabilities are a different animal entirely. Traditional web application failures like XSS and SQL injection still apply, but the API-specific failures demand different detection techniques, different controls, and a fundamentally different mental model of what “attack surface” means.
The Authorization Problem
BOLA consistently ranks as the most critical API vulnerability because it is trivially exploitable by any authenticated user and nearly invisible to automated scanning. This is the mistake that catches every team eventually.
The pattern is dead simple. An API endpoint accepts a resource identifier: GET /api/invoices/84721. The server validates the authentication token, confirms the user is logged in, and returns the invoice. What it never checks is whether the authenticated user has any legitimate relationship to invoice 84721. An attacker who is a legitimate customer just iterates invoice IDs and reads other customers’ billing data. That is the whole exploit.
Fixing BOLA requires authorization logic at the data access layer, not the request layer. The service must verify that the authenticated identity has a specific, documented relationship to the requested resource. In practice, your database query needs a WHERE user_id = $authenticated_user clause, or your ORM uses a scoped query builder that automatically filters by tenant. This check happens in application code. The API gateway cannot make this determination because it lacks the business rules. This is exactly why BOLA survives security reviews that only focus on gateway configuration.
Build an authorization middleware that every endpoint uses by default, where the developer must explicitly declare the ownership relationship (e.g., @authorize(resource="invoice", owner_field="user_id")). Make the insecure path require effort. Make the secure path the default. For how this fits into a broader zero trust authorization model, see the zero trust architecture guide.
Broken Function Level Authorization is the related failure: users accessing administrative endpoints because developers assumed obscurity would prevent discovery. It never does. Attackers enumerate API paths systematically using wordlists, and framework-generated endpoints like /actuator, /debug, and /admin are tried on every target.
Defense-in-Depth Across Layers
BOLA is the headline vulnerability, but it is far from the only one. Here is how the defense actually layers together in production.
Schema validation at the gateway enforces your OpenAPI spec on every inbound request and catches mass assignment attacks before they reach application code. If the spec declares that POST /users accepts name and email, the gateway rejects any request that includes role, admin, or other undocumented fields. This is one of the cheapest and most effective controls you have. It is routinely found unconfigured even at companies that have a complete OpenAPI spec checked into their repository. It takes 30 minutes to enable on Kong, AWS API Gateway, or Envoy. Thirty minutes, and it blocks an entire class of privilege escalation attacks. There is no excuse for leaving it off.
Rate limiting degrades enumeration attacks. An attacker iterating invoice IDs at 10,000 requests per second downloads your entire customer database before anyone notices. The same attack throttled to 10 requests per second gives your anomaly detection time to surface the pattern. Be clear about what rate limits do and do not do. They do not prevent BOLA. Only proper authorization logic does that. But they shrink the window for mass data exposure from minutes to hours, and that is the difference between a contained incident and a reportable breach.
Anomaly detection on API access patterns catches BOLA exploitation even when authorization controls are incomplete. A single user suddenly requesting 500 unique invoice IDs in an hour is a clear signal. Build alerting on per-user unique resource ID access rates, not just aggregate traffic volume. That distinction matters. Aggregate metrics will look completely normal while one user silently exfiltrates your entire dataset.
If you are investing in application security, get schema validation, rate limiting, and anomaly detection in place before you even think about penetration tests. Pen testers finding BOLA through unthrottled enumeration is the expected outcome without these controls. You are paying someone to tell you what you should already know.
JWT and OAuth Implementation Failures
Token-based authentication is standard across enterprise APIs. And JWT implementations consistently introduce critical vulnerabilities that standard security reviews miss. Here are the ones that keep showing up.
The algorithm confusion attack is the one you need to understand cold. JWTs include an alg header declaring the signing algorithm. A server configured for RS256 (asymmetric, signed with a private key, verified with the public key) can be exploited by an attacker who crafts a token with alg: HS256. If the server uses the RS256 public key as the HMAC secret for the symmetric HS256 verification (which is technically valid HMAC), it accepts attacker-crafted tokens signed with the public key. The public key. The one anyone can download. The fix is simple: hard-code the expected algorithm in your JWT verification library. Never accept the algorithm from the token header.
Not validating the exp claim is equally common and easier to overlook. Some older JWT libraries do not enforce token expiry by default. Do not assume your library handles this. Check the documentation explicitly and write a test that verifies an expired token is rejected.
For OAuth flows: validate the iss (issuer) claim to ensure the token was issued by your expected identity provider, and validate the aud (audience) claim to ensure the token was issued for your specific application. A valid token issued by your dev environment’s identity provider should never be accepted by production APIs. This exact misconfiguration appears in production at companies that otherwise take security seriously.
API Inventory and Shadow APIs
You cannot secure what you do not know exists. And most enterprises have a 30-40% gap between their official API inventory and the endpoints actually accessible in production. That is not a typo. A third of your attack surface is invisible to you right now.
Accurate inventory requires passive traffic analysis. Capture what your API gateways and load balancers are actually receiving, then compare against what your OpenAPI specs document. Anything the gateway routes but the spec does not describe is a shadow API candidate: legacy versioned endpoints, framework-generated management endpoints, third-party library APIs, endpoints from a previous architecture that nobody decommissioned.
Once the inventory is accurate, the security program can finally be coverage-based: are all known endpoints behind authentication? Are all endpoints included in penetration testing scope? Are deprecated endpoints actively decommissioned on their announced sunset dates?
That last point is where programs consistently fall short. Deprecated endpoints stay running because removing them “might break something.” Stop doing this. Every active endpoint, documented or not, is attack surface that requires monitoring, patching, and security maintenance. Build automated sunset enforcement into the gateway: deprecated endpoints start returning Sunset headers 90 days before shutdown, return 410 Gone after the deadline, and usage dashboards track consumer migration. The alternative, leaving old versions running indefinitely, is how you end up with API sprawl that no security team can effectively cover.
For teams building new APIs or modernizing existing ones, our guide to container security covers how to protect the runtime environment where your API services actually execute.