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.

Member Exclusive Free Tutorial

This chapter is free exclusive content for registered members! Please login or register to unlock immediately.

Login / Register Now