API Security Testing Is Not Just Web App Testing with JSON
The developers built an API. They put it behind authentication, used HTTPS everywhere, and validated inputs on the important fields. The web application on top of it had a penetration test last quarter. Security considers it covered.
The API had its own endpoints. Different consumers, different authentication flows, different behaviors. None of them were in scope for the web application test. Nobody ran a tool against them. Nobody looked at the documentation. Nobody sent a malformed request and watched what came back.
This is how most API attack surface ends up untested: not through neglect exactly, but through a scoping assumption that a web application pentest covers the application and the API underneath it simultaneously. It does not. An API has a different attack surface than the application layer that calls it, and the vulnerabilities that live there are different from what web application testing looks for.
The Inventory Problem Comes First
Before anything else, the assessment needs to establish what endpoints exist.
This is not as simple as it sounds. Organizations frequently cannot provide a complete API inventory. The swagger documentation covers the main product API. The internal admin API is on a different port and not documented. The legacy API from before the v2 rewrite is still accessible at /api/v1/ because somebody depended on it. The partner API is documented separately and nobody thought to include it.
An API assessment that starts from "here is our swagger.json" is working from an incomplete picture. The first step is discovery: crawling the application, pulling JavaScript bundles to find hardcoded endpoint paths, checking for common patterns (/api/, /v1/, /v2/, /internal/), and enumerating everything that responds. The undiscovered endpoints are frequently the most interesting ones.
Broken Object Level Authorization at API Scale
BOLA - Broken Object Level Authorization - is the vulnerability class that appears most consistently in API assessments and is most consistently underestimated before the test runs.
The pattern: APIs operate on objects. GET /api/orders/10042 returns an order. PATCH /api/users/445 modifies a user profile. POST /api/invoices/8821/payment processes a payment. Each endpoint takes an object identifier as input. When the authorization check is missing or incomplete, any authenticated user can substitute a different identifier and operate on objects that belong to someone else.
In a web application context, insecure direct object references are well understood. In an API context, the problem compounds because the same resource type may be reachable through multiple endpoints, nested resources create additional paths (GET /api/users/445/settings alongside GET /api/accounts/12/users/445/settings), and bulk operations introduce their own authorization gaps when a filter parameter is omitted and the endpoint returns everything. There is no HTML to visually confirm what is supposed to be accessible, so excessive access often goes unnoticed.
An API assessment that tests BOLA thoroughly requires two authenticated sessions at different privilege levels, a systematic approach to every endpoint that takes an identifier, and enough time to understand the resource model before assuming it is correctly implemented.
Mass Assignment: What Nobody Tested
Most frameworks bind incoming request body parameters directly to model objects. This is convenient for developers. It creates a vulnerability when clients can set fields they are not supposed to control.
A POST /api/users/register endpoint that accepts {"email": "user@example.com", "password": "..."} probably works correctly. The same endpoint that also accepts {"role": "admin", "account_status": "verified", "subscription_tier": "enterprise"} and applies those fields without checking whether the client should be able to set them - that is mass assignment.
The attack works by discovering the full object model. The API returns a user object with fifteen fields. The client-side code only populates three of them. A tester sends the registration request with all fifteen populated. The server accepts all of them. The account is now an admin.
Mass assignment almost never appears in scanner output because the scanner does not know which fields are writable by which clients. It requires a tester who understands what the API returns versus what the client sends, and who checks whether the gap between those two things is exploitable.
Zombie Endpoints and API Versioning
APIs get versioned. The v1 API gets replaced by v2. The v2 clients get migrated. Someone puts a sunset date on the documentation.
The v1 endpoint stays live.
This happens because decommissioning an API endpoint requires confirming nothing still depends on it, and that confirmation is surprisingly difficult to make with certainty. Load balancers get updated but routing rules get missed. The deprecated version stays deployed "just in case" for six months and then becomes part of the infrastructure nobody wants to touch.
v1 APIs frequently lack the security controls added in v2. Rate limiting that was bolted on later. Authentication improvements that were not backported. Input validation that was tightened after an audit. The zombie version of the API carries the attack surface from before those improvements were made.
An API assessment that only tests the current documented version misses the most likely place to find older vulnerabilities. Discovery that includes all version paths (/v1/, /v2/, /api/v1/, /rest/v1/, /api/legacy/) is part of the scope, not an optional extension.
Excessive Data Exposure
APIs commonly return more data than the client displays. The endpoint returns a user object with forty fields. The mobile app shows five of them. Nobody looked at what the other thirty-five contained.
In a web application context, what is rendered in the browser is roughly what was transmitted. In an API context, the gap between what is returned and what is displayed can be substantial, and the difference includes things like internal account flags, customer IDs used in other systems, relationship identifiers for objects the client should not know about, and metadata that creates a secondary attack surface.
A common pattern: an API returns the full user record for every response that involves a user - including password reset confirmations, account lookup results, and list endpoints. The client shows the name and email. The response also contains the user's role, internal flags, linked account IDs, and sometimes a temporary token.
Excessive data exposure is not a scanner finding. It requires an assessor who intercepts responses, reads them, and evaluates whether the fields present represent information the client should have access to.
What Good Scope Looks Like
A properly scoped API assessment specifies which API versions are in scope, provides authenticated credentials at each relevant privilege level, includes endpoints that require elevated privileges, and allocates enough time for the assessor to understand the resource model before testing authorization against it.
The deliverable should include coverage of BOLA across major resource types, mass assignment testing on create and update endpoints, enumeration of accessible API versions, and review of response content for excessive disclosure. A scanner report appended to the end is not a substitute for any of that.
Most API vulnerabilities that matter cannot be found without a tester who understands the application's intent and tests against it deliberately. That is what separates an API assessment from an API scan, and the gap between those two outputs is usually where the critical findings live.