Overview
This engagement walks every documented endpoint and every undocumented one that can be discovered from JavaScript bundles, mobile binaries, historical URL archives and GraphQL introspection, then tests each against the OWASP API Security Top 10 with at least two accounts in hand.
Where a specification exists it is used as a starting point, not a boundary. Shadow endpoints, deprecated versions still routed in production and internal-only methods reachable from outside are a recurring source of critical findings.
Engagement at a glance
- Typical duration
- 4 to 12 working days, depending on endpoint count
- Protocols
- REST, GraphQL, gRPC, WebSocket, webhooks and server-sent events
- Inputs
- OpenAPI/Swagger, Postman collection or GraphQL introspection, or discovery from traffic if none exist
- Standards
- OWASP API Security Top 10 (2023), OWASP ASVS
- Deliverable
- Per-endpoint coverage matrix plus reproducible request/response pairs
- Retest
- Included
What is covered
Every item below is tested and recorded, so the report shows what held as clearly as what failed.
- Broken object-level authorisation (BOLA/IDOR)
- Broken function-level authorisation (BFLA)
- Mass assignment and parameter pollution
- Excessive data exposure in responses
- Authentication, JWT and API-key handling
- GraphQL introspection, alias batching and field-level authorisation
- Rate limiting and resource consumption
- HTTP method override and verb tampering
- Webhook and callback abuse
- Versioned and shadow endpoint discovery
- Injection through API parameters
- Server-side request forgery via API inputs
Test matrix
What is attempted in each class, and what it means when it works.
| Class | What is attempted | Typical impact |
|---|---|---|
| Object-level authorisation | Every identifier in every response replayed from a second account, including UUIDs and encoded references | Cross-customer data access at scale |
| Function-level authorisation | Administrative methods called with standard-user credentials; role checks tested per method, not per route | Privilege escalation to administrator |
| Mass assignment | Response fields echoed back into write requests: role, tenant, balance, verified flags | Self-promotion to admin, balance and status manipulation |
| JWT handling | Algorithm confusion, alg:none, kid traversal, jku injection, weak HMAC secrets, refresh-token replay after logout | Forged identity, session that outlives revocation |
| GraphQL | Introspection exposure, alias batching against rate limits, field-level authorisation, query depth and cost | Authorisation bypass, brute force amplification, denial of service |
| Data exposure | Full object serialisation, internal fields, debug payloads, verbose errors and stack traces | PII disclosure, internal architecture mapped for follow-on attack |
| Rate limiting | Per-endpoint limits, header-based bypass, case and path variation, single-packet race windows | Credential stuffing, coupon and quota abuse |
What we commonly find
UUIDs treated as an authorisation control
Unguessable is not unauthorised. Identifiers leak through exports, notifications, audit trails and referral links, and then the object is readable by anyone holding one.
Authorisation implemented on the route, not the object
The middleware confirms the caller is authenticated and the role is correct, then the handler loads whatever id it was given.
Deprecated versions still routed
/api/v1 remains reachable years after v2 shipped, carrying the authorisation bugs that v2 fixed.
GraphQL aliases defeating rate limits
One request, two hundred aliased mutations. Per-request limiting counts it once.
How the engagement runs
Scoping and threat modelling
Map the asset, the attacker profile, and what “compromised” actually means for this business. Rules of engagement, testing windows, excluded techniques and an escalation contact are agreed in writing before anything is sent.
Reconnaissance and surface mapping
Enumerate everything reachable: subdomains from multiple passive sources, every endpoint referenced in JavaScript bundles, exposed services, third-party integrations, and the assets nobody remembers deploying. Coverage is recorded per host, so what was not tested is as visible as what was.
Manual exploitation
Authenticated testing from every role, with at least two accounts per role. Business logic, authorisation boundaries, injection, race conditions and state transitions, with each candidate reproduced live before it is written down. Automated tooling contributes coverage; it never contributes findings.
Verification and impact
Every finding is reproduced in a fresh session, isolated to the single parameter that causes it, and pushed to its maximum realistic impact. A finding that cannot survive a clean-room reproduction does not appear in the report.
Reporting and retest
The report is written twice over: once for the engineer who has to fix it, once for the auditor who has to file it. A walkthrough session follows, then a retest of every finding, closed only when re-exploitation fails.
What you receive
Executive summary
One page for the people who approve budget: what was tested, what was found, what it means in business terms.
Technical findings
Each finding with severity, CVSS, affected component, full request and response, reproduction steps and a working proof of concept.
Attack chains
Where findings combine, the chain is written out end to end, from first request to demonstrated impact.
Remediation guidance
A specific fix for your stack and framework, with the corrected pattern, not a link to a generic reference page.
Audit mapping
Findings mapped to SOC 2, ISO 27001, PCI DSS, HIPAA and OWASP ASVS as applicable, so the report drops straight into an audit pack.
Retest and attestation
Every finding retested in a clean session after remediation, with a signed attestation letter for customers and auditors.
When to run it
- Before publishing a public or partner API.
- After adding a new authentication scheme, tenancy model or permission tier.
- When a mobile application is the main consumer, because the API is the real attack surface, not the app.
- Annually alongside the web application test, or continuously for APIs under active development.
Questions
We have no API documentation. Is that a problem?
No. Endpoints are recovered from JavaScript bundles, mobile binaries, historical URL archives, GraphQL introspection and observed traffic. Undocumented endpoints are frequently the most productive part of the engagement. They are the ones nobody has reviewed.
Do you test GraphQL differently from REST?
Yes. GraphQL moves authorisation to the field level, so every type and field is tested independently rather than per route. Introspection, alias batching, query depth and cost limits, persisted-query CSRF and subscription authorisation over WebSocket are all specific to GraphQL and all covered.
Can you test an API that requires mutual TLS or signed requests?
Yes, provide the client certificate or signing key and the signing procedure. Request signing is typically implemented in a client library that can be driven directly, and custom tooling is written for the engagement where needed.
How do you avoid polluting our production data?
Write operations are confined to records created during the engagement wherever possible, every created object is tagged with an agreed marker, and a cleanup list is delivered with the report. Destructive operations against existing records are performed only with written approval.
How much does API penetration testing cost?
Scope drives the number: how many endpoints, how many roles and tenants, whether the API is public or internal, and whether specifications exist. A fixed quote follows a short scoping call. Undocumented endpoints discovered during testing are covered by the agreed scope rather than billed as extra.
Do you test against the OWASP API Security Top 10?
Yes, as the coverage baseline. Every endpoint is tested against all ten categories with at least two accounts in hand, because the first three, broken object-level authorisation, broken authentication and broken object property-level authorisation, are only observable when you can compare what two different users can reach.
How do you find endpoints we have not documented?
From JavaScript bundles, mobile binaries, historical URL archives, GraphQL introspection and error responses that leak route names. Undocumented endpoints are usually the interesting ones: they were built for an internal tool, never got the authorisation middleware, and were never taken down.
Can you test webhooks and callbacks?
Yes. Webhook verification is a recurring source of financial loss: signatures that are checked only when present, replay windows that never expire, and callback handlers that trust a status field supplied by the caller. Testing covers whether your handler can be made to act on a request you did not send.
Do you test rate limiting and abuse controls?
Yes, within limits agreed in writing. The question is not whether a limit exists but whether it can be bypassed by rotating a header, changing a case, or racing two requests through the check. Volumetric denial of service is out of scope by default and only performed on written request against a non-production target.