APIs have quietly become the most attacked surface in modern applications. Every mobile app, every microservice, every third-party integration—they all talk through APIs. And most of them are broken in predictable ways.
This post covers the OWASP API Security Top 10 (2023 edition), how I approach testing each one, and some real breaches that show why this stuff matters.
Part 2 of this series is a hands-on cheat sheet with actual commands you can use during testing.
Quick Reference
| # | Vulnerability | One-Liner |
|---|---|---|
| API1 | BOLA | Change object ID, access other users’ data |
| API2 | Broken Authentication | JWT attacks, weak tokens, missing validation |
| API3 | Broken Object Property Level | Mass assignment, excessive data exposure |
| API4 | Unrestricted Resource Consumption | No rate limits, DoS via expensive queries |
| API5 | BFLA | Regular user accessing admin endpoints |
| API6 | Unrestricted Access to Business Flows | Automating purchases, referral abuse |
| API7 | SSRF | Fetch internal resources via URL parameters |
| API8 | Security Misconfiguration | CORS, verbose errors, debug endpoints |
| API9 | Improper Inventory Management | Shadow APIs, unprotected old versions |
| API10 | Unsafe Consumption of APIs | Trusting third-party data without validation |
Why API Security is Different
Traditional web app testing focuses on things like XSS, CSRF, and session management. API testing overlaps but has its own flavor:
- Authentication is token-based — JWTs, API keys, OAuth tokens instead of session cookies
- Authorization failures are everywhere — BOLA/IDOR is the #1 API vulnerability by a huge margin
- The attack surface is hidden — No UI to click through, you’re working blind without documentation
- Microservices multiply risk — One app might have 50 internal APIs, each a potential entry point
API1:2023 — Broken Object Level Authorization (BOLA)
TL;DR: Change an object ID in a request while keeping your auth token. If you get someone else’s data, it’s vulnerable.
This is the big one. Roughly 40% of all API attacks exploit BOLA.
| Attack | Detect | Defend |
|---|---|---|
| Swap object IDs between users | Alert on user accessing >10 unique object IDs/min | Validate object ownership on every request |
| Enumerate sequential/predictable IDs | Log all object access with user + object context | Use indirect references (session-scoped mappings) |
| Test encoded IDs (base64, UUID) | Monitor for horizontal access patterns | Implement attribute-based access control (ABAC) |
Testing Checklist
- Identify endpoints with object IDs (URL path, query params, JSON body)
- Create two test accounts (User A, User B)
- Capture request containing User A’s object ID
- Replay request with User B’s token + User A’s object ID
- Test ID variations:
0,1,-1,admin,null,../ - Check encoded formats: base64 decode → modify → re-encode
- Use Burp Autorize for automated cross-session testing
Where to Look
- URL path parameters (
/api/orders/9876) - Query strings (
?user_id=42) - JSON request bodies (
{"recipient_id": "5432"}) - Headers (less common but happens)
API2:2023 — Broken Authentication
TL;DR: Forge or crack tokens, exploit weak JWT implementations, bypass authentication entirely.
This covers everything wrong with how APIs verify identity: weak JWT implementations, missing token validation, credential stuffing vulnerabilities.
| Attack | Detect | Defend |
|---|---|---|
| JWT “none” algorithm attack | Alert on tokens with alg=none | Whitelist allowed algorithms server-side |
| Algorithm confusion (RS256→HS256) | Log algorithm mismatches | Use asymmetric keys, validate algorithm |
| Crack weak JWT secrets | Monitor for high-volume token validation failures | Use strong secrets (256+ bits of entropy) |
| Replay expired/revoked tokens | Track token reuse after logout | Implement token blacklisting, short expiry |
| kid parameter injection (SQLi/path traversal) | Log unusual kid values | Sanitize kid, use key lookup tables |
Testing Checklist
- Decode JWT and inspect header + payload
- Test
alg: noneattack (remove signature) - Try algorithm confusion (RS256 → HS256 with public key as secret)
- Crack weak secrets with hashcat/jwt_tool
- Test kid parameter for SQLi and path traversal
- Replay expired tokens
- Use token after logout
- Check for missing rate limiting on login
JWT Attack Details
The “none” algorithm: Change "alg": "HS256" to "alg": "none" and delete the signature. Some servers accept this.
Algorithm confusion: If the server uses RS256 (asymmetric), try switching to HS256 (symmetric) and sign with the public key as the secret.
kid injection examples:
{"alg":"HS256", "kid":"key1' UNION SELECT 'secret'--"}
{"alg":"HS256", "kid":"../../../etc/passwd"}
API3:2023 — Broken Object Property Level Authorization
TL;DR: The API returns too much data or accepts fields it shouldn’t. Check responses for sensitive fields, try adding
"role": "admin"to requests.
This combines two related issues: Excessive Data Exposure and Mass Assignment.
| Attack | Detect | Defend |
|---|---|---|
| Inspect responses for sensitive fields | Log response sizes, flag abnormally large payloads | Return only necessary fields (DTO pattern) |
| Add privilege fields to requests (mass assignment) | Alert on unexpected fields in requests | Whitelist allowed input fields |
| Modify read-only fields (price, balance, role) | Track field modification patterns | Use separate read/write models |
Testing Checklist
- Inspect all API responses for sensitive fields (password_hash, ssn, internal_id, role)
- Compare response fields vs what the UI displays
- Try adding
"role": "admin"to registration/update requests - Test common fields:
isAdmin,verified,credits,discount,price - Use Arjun to discover hidden parameters
- Check if you can modify fields that should be read-only
Common Mass Assignment Fields
{"isAdmin": true}
{"role": "admin"}
{"permissions": ["read", "write", "delete"]}
{"verified": true}
{"credits": 99999}
{"discount": 100}
{"price": 0}
{"approved": true}
API4:2023 — Unrestricted Resource Consumption
TL;DR: No rate limiting, no pagination limits, no query complexity limits. Fire 100 requests and see if you get blocked.
| Attack | Detect | Defend |
|---|---|---|
| Brute force login (no rate limit) | Alert on >10 failed logins/min per user/IP | Implement rate limiting with exponential backoff |
Request excessive data (?limit=999999) | Monitor response sizes and query times | Enforce max page size server-side |
| GraphQL depth/batching attacks | Track query complexity scores | Implement query depth and complexity limits |
| Resource exhaustion via expensive operations | Monitor CPU/memory spikes per endpoint | Set timeouts, limit concurrent requests |
Testing Checklist
- Fire 100 login attempts in 10 seconds — any 429 responses?
- Test pagination:
?limit=999999,?per_page=-1 - For GraphQL: send deeply nested queries
- Try GraphQL batching to bypass rate limits
- Check if expensive operations (reports, exports) are rate limited
- Test file upload size limits
GraphQL Batching Bypass
Rate limiting often counts requests, not operations. This single request contains multiple login attempts:
mutation {
a: login(user:"admin", pass:"pass1") { token }
b: login(user:"admin", pass:"pass2") { token }
c: login(user:"admin", pass:"pass3") { token }
}
API5:2023 — Broken Function Level Authorization (BFLA)
TL;DR: BOLA is about accessing other users’ data. BFLA is about accessing other users’ functions. Can a regular user hit admin endpoints?
| Attack | Detect | Defend |
|---|---|---|
| Access admin endpoints with user token | Alert on non-admin accessing /admin/* paths | Implement role-based access control (RBAC) |
| HTTP method tampering (GET→DELETE) | Log method changes on same endpoint | Validate permissions per method |
| Discover internal/debug endpoints | Monitor for 403s on sensitive paths | Remove or protect internal endpoints |
Testing Checklist
- Fuzz for admin paths:
/api/admin/*,/api/internal/*,/api/debug/* - Try accessing admin endpoints with regular user token
- Test HTTP method tampering: if GET works, try DELETE/PUT/PATCH
- Check if different Content-Types bypass authorization
- Use kiterunner for endpoint discovery
- Compare functionality between user roles
Common Admin Paths
/api/admin/users
/api/admin/config
/api/internal/metrics
/api/debug
/api/management/users
/api/v1/admin/
API6:2023 — Unrestricted Access to Sensitive Business Flows
TL;DR: Can you script actions that should require human interaction? Automate purchases, abuse referrals, spam reviews.
| Attack | Detect | Defend |
|---|---|---|
| Automate limited-item purchases (scalping) | Detect rapid sequential purchases | Implement CAPTCHA, device fingerprinting |
| Abuse referral/promo codes | Track referral patterns, flag self-referrals | One-time use codes, user-code binding |
| Create fake accounts in bulk | Monitor registration velocity | Email verification, rate limiting |
| Spam reviews/ratings | Detect duplicate content, timing patterns | Require purchase verification for reviews |
Testing Checklist
- Can you automate the checkout flow?
- Test coupon/promo code reuse
- Try referring yourself
- Can you create accounts without email verification?
- Test for race conditions in financial operations
- Check if human verification (CAPTCHA) can be bypassed
API7:2023 — Server-Side Request Forgery (SSRF)
TL;DR: Any parameter that takes a URL is an SSRF candidate. Test with cloud metadata endpoints.
| Attack | Detect | Defend |
|---|---|---|
| Fetch cloud metadata (AWS/GCP/Azure) | Alert on requests to 169.254.169.254 | Block internal IP ranges |
| Scan internal ports | Monitor for internal IP access patterns | Whitelist allowed domains |
| Bypass URL filters (decimal IP, IPv6) | Log URL normalization mismatches | Validate and normalize URLs server-side |
| Exfiltrate data via DNS/HTTP callbacks | Monitor outbound DNS queries | Disable unnecessary URL fetching |
Testing Checklist
- Find URL input parameters (webhooks, imports, previews)
- Test AWS metadata:
http://169.254.169.254/latest/meta-data/ - Test GCP metadata:
http://metadata.google.internal/... - Try filter bypasses: decimal IP, IPv6, URL encoding
- Scan internal ports via SSRF
- Use Burp Collaborator for blind SSRF
URL Filter Bypasses
http://2130706433/ (decimal IP = 127.0.0.1)
http://[::1]/ (IPv6 localhost)
http://127.0.0.1%00@evil.com/
http://localhost%23@evil.com/
http://127.1/
http://0/
API8:2023 — Security Misconfiguration
TL;DR: The grab bag of “you left something open.” Test CORS, find exposed docs, trigger verbose errors.
| Attack | Detect | Defend |
|---|---|---|
| Exploit CORS misconfiguration | Monitor Origin header variations | Whitelist specific origins, avoid wildcards |
| Access exposed API documentation | Alert on swagger.json/openapi.json access | Protect or remove docs in production |
| Extract info from verbose errors | Log error response patterns | Use generic error messages |
| Exploit debug endpoints | Monitor for debug/test path access | Remove debug code in production |
Testing Checklist
- Test CORS: send
Origin: https://evil.com, check response headers - Find exposed docs:
/swagger.json,/api-docs,/graphql - Trigger verbose errors with malformed input
- Check for debug endpoints
- Verify unnecessary HTTP methods are disabled
- Check security headers (HSTS, X-Content-Type-Options)
CORS Vulnerability Check
Send request with Origin: https://evil.com. Vulnerable response:
Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: true
Also test: Origin: null, Origin: https://target.com.evil.com
API9:2023 — Improper Inventory Management
TL;DR: Shadow APIs and forgotten versions. The new v2 has auth, but v1 is still running without it.
| Attack | Detect | Defend |
|---|---|---|
| Access old API versions without auth | Monitor traffic to deprecated versions | Decommission old versions completely |
| Discover shadow/undocumented APIs | Track requests to unknown endpoints | Maintain API inventory |
| Exploit beta/test endpoints | Alert on non-production path access | Isolate test environments |
Testing Checklist
- Enumerate versions:
/api/v1/,/api/v2/,/api/beta/ - Compare security between versions (auth, rate limits)
- Use kiterunner to discover shadow APIs
- Check subdomains:
api-dev.,api-staging.,internal-api. - Look for documentation of deprecated endpoints
- Test if old versions have weaker security controls
API10:2023 — Unsafe Consumption of APIs
TL;DR: What happens when the API consumes external data? If it trusts third-party data without validation, inject payloads through the back door.
| Attack | Detect | Defend |
|---|---|---|
| Poison webhook callback data | Validate webhook signatures | Verify webhook sources, validate payloads |
| Inject payloads via third-party integrations | Monitor for injection patterns in external data | Sanitize all external input |
| Exploit trust in partner APIs | Log third-party API response anomalies | Apply same validation as user input |
Testing Checklist
- Identify third-party data sources (webhooks, imports, feeds)
- If you control a webhook endpoint, return malicious payloads
- Test if external data is validated (XSS, SQLi, command injection)
- Check if third-party API failures are handled securely
- Look for SSRF via third-party URL parameters
Real-World Breaches
Optus (2022) — 9.8 Million Records
This one had everything wrong:
- A test API endpoint left publicly exposed
- A 2018 coding error disabled access controls
- Customer IDs were sequential (incrementing by 1)
- No rate limiting
- No authentication required
An attacker just iterated through IDs and downloaded everything. The breach led to a $1.5M ransom demand and new Australian cybersecurity legislation.
Parler (2021) — 70TB of Data
Researchers archived the entire platform before it went offline:
- No authentication on public post APIs
- Sequential post IDs enabled complete enumeration
- No rate limiting
- Raw media files retained GPS metadata
- “Deleted” posts were only marked, not actually removed
The data was later used in legal investigations.
T-Mobile (2023) — 37 Million Customers
Attackers exfiltrated customer data over two months through a vulnerable API endpoint. The extended dwell time shows monitoring was inadequate—they had no idea someone was bulk-downloading their customer database.
My Testing Methodology
| Phase | Focus | Tools |
|---|---|---|
| 1. Reconnaissance | Find docs, enumerate endpoints, discover parameters | Kiterunner, Arjun, ffuf |
| 2. Authentication | JWT attacks, token lifecycle, signature validation | jwt_tool, Burp |
| 3. Authorization | BOLA/BFLA testing across privilege levels | Autorize, manual testing |
| 4. Input Validation | Injection testing adapted for JSON payloads | Burp Intruder, sqlmap |
| 5. Business Logic | Workflow manipulation, race conditions, abuse scenarios | Turbo Intruder, custom scripts |
What’s Next
In Part 2, I break down each vulnerability into step-by-step testing commands you can use during engagements. It’s the practical cheat sheet I wish I had when I started learning this stuff.
Questions or feedback? Find me on GitHub.