# Play: API Security Review Comprehensive API security assessment against OWASP API Security Top 10 (2023) with real-world attack scenarios, automated testing integration, and detailed remediation guidance. ## Trigger Conditions Use this play when: - Reviewing OpenAPI/Swagger specifications or API documentation - Auditing REST, GraphQL, gRPC, or SOAP API implementations - Testing API authentication and authorization mechanisms - Reviewing API gateway configurations (Kong, nginx, Envoy, AWS API Gateway) - Conducting pre-deployment security assessments - Performing API penetration testing or security audits - A user asks to "test my API for security issues" ## Inputs - OpenAPI/Swagger specification (YAML/JSON) or API documentation - API source code (routes, controllers, middleware, authentication logic) - API gateway and infrastructure configurations - Authentication implementation details (JWT, OAuth, API keys) - Environment information (production, staging, development) - Previous security assessment reports (if available) ## Procedure ### Phase 1: Discovery & Reconnaissance #### 1.1 API Surface Enumeration **With OpenAPI Spec:** ```bash # Parse and analyze OpenAPI spec # Look for: - Total endpoints and HTTP methods - Authentication requirements per endpoint - Request/response schemas - Rate limiting documentation - Deprecated endpoints ``` **Without OpenAPI Spec (Code Analysis):** - Scan route definitions in framework-specific patterns: - Express: `app.get()`, `router.post()` - Django: `path()`, `url()` - Rails: `resources`, `get/post/put/delete` - Spring: `@GetMapping`, `@RequestMapping` - FastAPI: `@app.get()`, decorator-based routes **Key Questions:** - How many endpoints exist? - Which require authentication vs. public? - What data types are exchanged? - Are there file upload endpoints? - Are there admin/debug endpoints? #### 1.2 Authentication Mechanism Identification Document all auth mechanisms in use: | Mechanism | Indicators | Security Considerations | |-----------|-----------|------------------------| | **JWT** | `Authorization: Bearer eyJ...` | Algorithm, signing key strength, expiration | | **OAuth 2.0** | `/oauth/authorize`, `/oauth/token` | Flow type, PKCE, state parameter, redirect URI | | **API Keys** | `X-API-Key`, `api_key` query param | Key rotation, transmission security, scope | | **Session Cookies** | `Set-Cookie: sessionid=...` | HttpOnly, Secure, SameSite, expiration | | **mTLS** | Client certificate requirements | Certificate validation, revocation | | **Basic Auth** | `Authorization: Basic base64` | HTTPS enforcement, credential storage | ### Phase 2: Authentication Security Assessment #### 2.1 JWT Security Testing **Common JWT Vulnerabilities:** 1. **Algorithm Confusion (CVE-2015-9235)** ``` Attack: Change algorithm from RS256 to none or HS256 Header: {"alg":"none","typ":"JWT"} Result: Token accepted without verification ``` 2. **Weak Signing Keys** ```bash # Test for weak secrets if using HS256 # Use jwt_tool or hashcat for brute force jwt_tool.py token_here -C -d /usr/share/wordlists/rockyou.txt ``` 3. **Missing Expiration** ``` Check: Does payload contain "exp" claim? Risk: Tokens valid forever if stolen ``` 4. **Token Storage Issues** ```javascript // Insecure: localStorage (XSS accessible) localStorage.setItem('jwt', token); // More secure: httpOnly cookie document.cookie = "jwt=" + token + "; HttpOnly; Secure; SameSite=Strict"; ``` **Testing Checklist:** - [ ] Test algorithm confusion (change alg to "none") - [ ] Attempt key confusion attacks (RS256 → HS256) - [ ] Check for missing expiration (exp claim) - [ ] Verify signature validation cannot be bypassed - [ ] Test token revocation mechanisms - [ ] Check for sensitive data in JWT payload #### 2.2 OAuth 2.0 Security Testing **Critical Checks:** 1. **Authorization Code Flow** ``` Required: PKCE (Proof Key for Code Exchange) Check: code_challenge and code_verifier parameters Risk: Without PKCE, auth codes can be stolen and used ``` 2. **State Parameter** ``` Required: Random state parameter to prevent CSRF Check: Is state validated on callback? Attack: Omit state → CSRF login attack ``` 3. **Redirect URI Validation** ``` Check: Is redirect_uri strictly validated? Attack: redirect_uri=https://attacker.com/callback Risk: Authorization code/token sent to attacker ``` 4. **Scope Validation** ``` Check: Can user modify requested scopes? Attack: Change scope=read to scope=admin Risk: Privilege escalation via scope manipulation ``` **Testing Checklist:** - [ ] Verify PKCE implementation for public clients - [ ] Test state parameter CSRF protection - [ ] Attempt redirect URI manipulation - [ ] Test scope escalation attacks - [ ] Check for open redirect vulnerabilities - [ ] Verify token endpoint authentication #### 2.3 API Key Security **Common Issues:** 1. **Key Exposure** ```bash # Search for hardcoded keys grep -r "api_key\|apikey\|api-key" --include="*.js" --include="*.py" --include="*.env" # Check client-side code # API keys in frontend code can be stolen ``` 2. **Insufficient Scope** ``` Issue: Single key with full access Best Practice: Scoped keys (read-only, write, admin) ``` 3. **Missing Rotation** ``` Check: Can keys be rotated without downtime? Risk: Compromised keys valid indefinitely ``` ### Phase 3: OWASP API Top 10 Deep Assessment #### API1: Broken Object Level Authorization (BOLA/IDOR) > **Risk**: Attackers access other users' data by manipulating object IDs **Real-World Impact**: - **2019**: First American Financial exposed 885 million records via IDOR - **2020**: Twitter API IDOR allowed viewing protected tweets - **2021**: Facebook IDOR exposed private posts **Attack Scenarios:** 1. **Sequential ID Manipulation** ```http GET /api/users/123/orders # Try: GET /api/users/124/orders # Access other user's orders GET /api/users/1/orders # Access admin's orders ``` 2. **UUID Predictability** ``` If using predictable UUIDv1 (timestamp-based): Generate nearby UUIDs to find other users ``` 3. **Batch Endpoint Bypass** ```http POST /api/orders/bulk { "order_ids": [1001, 1002, 1003, 1004] # Can access any IDs? } ``` 4. **Indirect Object References** ```http GET /api/orders?id=1001 # vs GET /api/orders?id=1001&user_id=123 # Is user_id validated? ``` **Testing Approach:** 1. **Map Object IDs**: Identify all endpoints accepting object IDs 2. **Cross-User Testing**: - Create User A and User B accounts - Have User A create resources - Attempt to access with User B's credentials 3. **Batch Testing**: Test if batch endpoints filter by ownership 4. **File Access**: Test file download endpoints with different IDs 5. **IDOR Automation**: ```bash # Use Burp Intruder or custom script # Iterate through IDs and check for 200 responses for i in {1..1000}; do curl -H "Authorization: Bearer $TOKEN" \ "https://api.example.com/orders/$i" | grep -q "order_id" && echo "Found: $i" done ``` **Remediation:** ```python # BAD: No ownership check def get_order(order_id): return Order.objects.get(id=order_id) # GOOD: Verify ownership def get_order(order_id, user): return Order.objects.get(id=order_id, user=user) # Or: Check ownership after retrieval order = Order.objects.get(id=order_id) if order.user_id != user.id: raise PermissionDenied() return order ``` #### API2: Broken Authentication > **Risk**: Weak authentication allows account takeover **Real-World Impact**: - **2022**: Uber breach via compromised contractor account - **2023**: 23andMe breach via credential stuffing **Attack Scenarios:** 1. **Credential Stuffing** ```bash # Automated login attempts with breached credentials # APIs often lack CAPTCHA or rate limiting ``` 2. **JWT None Algorithm** ``` Header: {"alg":"none","typ":"JWT"} Payload: {"user_id":1,"role":"admin"} Signature: (empty) Result: Server accepts unsigned token ``` 3. **Weak Password Policy** ``` POST /api/login {"username":"admin","password":"123456"} # Test common passwords # Check minimum length requirements ``` 4. **Session Fixation** ``` Attacker sets session ID Victim logs in with attacker's session ID Attacker uses same session ID to access account ``` **Testing Approach:** 1. **Brute Force Testing**: ```bash # Test login endpoint rate limiting for i in {1..100}; do curl -X POST https://api.example.com/login \ -d "username=admin&password=wrong$i" done # Check if account locks or IP is blocked ``` 2. **Token Security**: - Decode JWT and analyze claims - Test algorithm confusion - Check expiration and refresh mechanisms - Test token revocation 3. **Password Policy**: - Test minimum length (should be 12+) - Test complexity requirements - Check common password blocking - Verify secure password reset flow 4. **Multi-Factor Authentication**: - Is MFA enforced for sensitive operations? - Can MFA be bypassed? - Are backup codes secure? **Remediation:** ```python # Implement rate limiting from django_ratelimit.decorators import ratelimit @ratelimit(key='ip', rate='5/m', method='POST') def login(request): # Login logic pass # Strong password validation from django.contrib.auth.password_validation import validate_password def validate_user_password(password): try: validate_password(password) except ValidationError as e: raise e # Secure JWT configuration JWT_SETTINGS = { 'ALGORITHM': 'HS256', # Use strong algorithms only 'VERIFY_EXPIRATION': True, 'EXPIRATION_DELTA': timedelta(minutes=15), 'REFRESH_EXPIRATION_DELTA': timedelta(days=7), } ``` #### API3: Broken Object Property Level Authorization > **Risk**: Mass assignment and data over-exposure **Real-World Impact**: - **2012**: GitHub mass assignment allowed unauthorized repo access - **2019**: Facebook mass assignment exposed user data **Attack Scenarios:** 1. **Mass Assignment** ```http POST /api/users { "username": "newuser", "email": "user@example.com", "password": "password123", "role": "admin", # Should not be user-settable "is_verified": true # Should not be user-settable } ``` 2. **Response Over-Exposure** ```http GET /api/users/123 Response: { "id": 123, "username": "john", "email": "john@example.com", "password_hash": "$2b$12$...", # Should not be exposed! "ssn": "123-45-6789", # PII exposure "internal_notes": "Difficult customer" } ``` 3. **Field-Level Authorization Bypass** ```http PUT /api/users/123 { "salary": 200000 # Can regular users modify this? } ``` **Testing Approach:** 1. **Mass Assignment Testing**: ```python # Send extra fields in POST/PUT requests payload = { "username": "test", "email": "test@test.com", "password": "password", "role": "admin", # Try to escalate "is_admin": True, "credit_balance": 10000 } ``` 2. **Response Analysis**: - Capture all API responses - Look for sensitive fields (passwords, tokens, PII) - Compare responses for different user roles - Check if internal fields are exposed 3. **GraphQL Over-Exposure**: ```graphql # Request fields you shouldn't see query { user(id: 123) { id name email password # Should be rejected ssn # Should be rejected salary # Should be rejected for non-admins } } ``` **Remediation:** ```python # Use allowlist approach ALLOWED_USER_FIELDS = ['username', 'email', 'password'] def update_user(user_id, data, current_user): # Filter input to allowed fields only filtered_data = {k: v for k, v in data.items() if k in ALLOWED_USER_FIELDS} # Check field-level permissions if 'role' in data and not current_user.is_admin: raise PermissionDenied("Cannot modify role") return User.objects.filter(id=user_id).update(**filtered_data) # Response serialization class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['id', 'username', 'email'] # Explicit allowlist # Never include: password, ssn, internal_notes ``` #### API4: Unrestricted Resource Consumption > **Risk**: DoS via resource exhaustion, financial drain **Real-World Impact**: - **2023**: OpenAI API abuse cost companies thousands - **2022**: GraphQL query DoS attacks on multiple platforms **Attack Scenarios:** 1. **Rate Limit Bypass** ```http # Distributed attack from multiple IPs # Or using different API keys # Or changing User-Agent headers ``` 2. **Expensive Query Abuse** ```graphql # Deeply nested GraphQL query query { users { posts { comments { author { posts { comments { author { posts { ... } # Infinite recursion potential } } } } } } } } ``` 3. **Large Payload Attacks** ```http POST /api/upload Content-Length: 1000000000 # 1GB file # Or JSON bomb: {"a":{"a":{"a":{"a":...}}}} # Deeply nested JSON ``` 4. **Pagination Abuse** ```http GET /api/items?limit=1000000 # Request all records GET /api/items?page=1&per_page=10000 # Large page size ``` **Testing Approach:** 1. **Rate Limit Testing**: ```bash # Test from single IP for i in {1..1000}; do curl https://api.example.com/data done # Test bypass methods: # - Different API keys # - Rotating User-Agents # - Distributed requests ``` 2. **GraphQL DoS**: ```bash # Test query complexity # Test query depth # Test expensive operations ``` 3. **Resource Exhaustion**: - Upload extremely large files - Send deeply nested JSON - Request massive result sets - Trigger expensive database queries **Remediation:** ```python # Rate limiting from django_ratelimit.decorators import ratelimit @ratelimit(key='user', rate='100/h', method=['GET', 'POST']) def api_endpoint(request): pass # GraphQL safeguards GRAPHQL_SETTINGS = { 'MAX_DEPTH': 10, 'MAX_COMPLEXITY': 1000, 'DISABLE_INTROSPECTION': True, # In production } # Pagination limits MAX_PAGE_SIZE = 100 DEFAULT_PAGE_SIZE = 20 def get_items(request): page_size = min( int(request.GET.get('limit', DEFAULT_PAGE_SIZE)), MAX_PAGE_SIZE ) return Item.objects.all()[:page_size] # File upload limits MAX_UPLOAD_SIZE = 10 * 1024 * 1024 # 10MB # Timeout configuration REQUEST_TIMEOUT = 30 # seconds ``` #### API5: Broken Function Level Authorization > **Risk**: Access to admin/sensitive functions without proper authorization **Real-World Impact**: - **2020**: Twitter admin tool breach - **2021**: Parler admin API exposed **Attack Scenarios:** 1. **Admin Endpoint Discovery** ```http GET /api/admin/users # Try common admin paths GET /api/v1/admin/config GET /api/internal/audit-logs GET /api/debug/info ``` 2. **HTTP Method Switching** ```http GET /api/users/123 # Allowed for regular users DELETE /api/users/123 # Should require admin, but is it checked? PUT /api/users/123/role # Can users change their own role? ``` 3. **Parameter-Based Access Control** ```http POST /api/users { "username": "test", "role": "admin" # Is this validated server-side? } ``` **Testing Approach:** 1. **Endpoint Enumeration**: ```bash # Use wordlists to find admin endpoints # Common paths: /admin, /internal, /debug, /system ``` 2. **Method Testing**: ```bash # For each endpoint, test all HTTP methods curl -X GET /api/resource curl -X POST /api/resource curl -X PUT /api/resource curl -X DELETE /api/resource curl -X PATCH /api/resource ``` 3. **Role Escalation**: - Create regular user account - Attempt to access admin endpoints - Try to modify role/privilege fields **Remediation:** ```python # Centralized authorization decorator from functools import wraps def require_admin(f): @wraps(f) def decorated_function(request, *args, **kwargs): if not request.user.is_admin: return Response( {"error": "Admin access required"}, status=403 ) return f(request, *args, **kwargs) return decorated_function @require_admin def admin_endpoint(request): pass # Middleware approach class AdminAuthorizationMiddleware: def __init__(self, get_response): self.get_response = get_response self.admin_paths = ['/api/admin/', '/api/internal/'] def __call__(self, request): if any(request.path.startswith(path) for path in self.admin_paths): if not request.user.is_admin: return HttpResponseForbidden() return self.get_response(request) ``` #### API6: Unrestricted Access to Sensitive Business Flows > **Risk**: Automated abuse of business logic **Real-World Impact**: - **Ticket scalping**: Bots buying all concert tickets - **E-commerce**: Inventory hoarding, price scraping - **Crypto**: Flash loan attacks via API manipulation **Attack Scenarios:** 1. **Inventory Exhaustion** ```http POST /api/cart/add {"product_id": 123, "quantity": 999999} # Buy all inventory ``` 2. **Automated Purchasing** ```http # Script that completes purchase faster than humans # Bypassing any client-side rate limits ``` 3. **Price Manipulation** ```http POST /api/order { "items": [...], "total": 1.00 # Is total calculated server-side? } ``` 4. **Reservation Abuse** ```http POST /api/reservations {"resource_id": 123, "start": "2024-01-01", "end": "2025-12-31"} # Reserve resource for entire year ``` **Testing Approach:** 1. **Business Logic Testing**: - Attempt to complete purchases with modified prices - Try to add negative quantities - Test race conditions in multi-step flows - Attempt to bypass payment steps 2. **Automation Testing**: ```python # Script to test if flows can be automated import requests import time start = time.time() for i in range(100): requests.post('/api/purchase', json={...}) elapsed = time.time() - start # If no rate limiting, 100 requests complete quickly ``` **Remediation:** ```python # Server-side price calculation def create_order(items, user): total = sum(item.product.price * item.quantity for item in items) # Never trust client-provided total # Inventory checks for item in items: if item.quantity > item.product.available_stock: raise ValidationError("Insufficient inventory") # Rate limiting per user if user.orders.filter(created_at__gte=timezone.now() - timedelta(hours=1)).count() > 10: raise RateLimitError("Too many orders") return Order.objects.create(user=user, items=items, total=total) # CAPTCHA for sensitive operations from django_recaptcha.fields import ReCaptchaField class PurchaseForm(forms.Form): recaptcha = ReCaptchaField() # ... other fields ``` #### API7: Server-Side Request Forgery (SSRF) > **Risk**: Server makes requests to attacker-controlled or internal resources **Real-World Impact**: - **2019**: Capital One breach via SSRF (100M records) - **2021**: Multiple cloud metadata service exploits **Attack Scenarios:** 1. **Cloud Metadata Access** ```http GET /api/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ # Retrieves AWS credentials ``` 2. **Internal Service Access** ```http GET /api/fetch?url=http://localhost:8080/admin GET /api/fetch?url=http://internal-api.company.local/secrets ``` 3. **URL Bypass Techniques** ``` http://127.0.0.1 → localhost bypass http://0177.0.0.1 → Octal encoding http://2130706433 → Decimal IP http://0x7f.0x0.0x0.0x1 → Hex encoding http://[::ffff:127.0.0.1] → IPv6 http://127.1 → Shortened ``` 4. **DNS Rebinding** ``` Attacker controls DNS that resolves to: - First request: legitimate.com → 1.2.3.4 - Second request: attacker.com → 169.254.169.254 ``` **Testing Approach:** 1. **Basic SSRF Testing**: ```bash # Test internal IP access curl "https://api.example.com/fetch?url=http://169.254.169.254/" curl "https://api.example.com/fetch?url=http://localhost:22" curl "https://api.example.com/fetch?url=file:///etc/passwd" ``` 2. **Bypass Testing**: ```bash # Try various encodings curl "https://api.example.com/fetch?url=http://0177.0.0.1/" curl "https://api.example.com/fetch?url=http://2130706433/" curl "https://api.example.com/fetch?url=http://[::ffff:127.0.0.1]/" ``` 3. **Port Scanning via SSRF**: ```bash # Test if server can reach internal ports for port in 22 80 443 3306 5432 6379 8080; do curl "https://api.example.com/fetch?url=http://localhost:$port" done ``` **Remediation:** ```python import ipaddress import re from urllib.parse import urlparse def is_internal_ip(ip_str): """Check if IP is internal/private""" try: ip = ipaddress.ip_address(ip_str) return ip.is_private or ip.is_loopback or ip.is_reserved except ValueError: return False def validate_url(url): """Validate URL for SSRF prevention""" parsed = urlparse(url) # Allowlist approach (recommended) allowed_hosts = ['api.trusted.com', 'cdn.trusted.com'] if parsed.hostname not in allowed_hosts: raise ValidationError("URL not in allowlist") # Blocklist approach (supplementary) blocked_schemes = ['file', 'ftp', 'gopher', 'dict'] if parsed.scheme in blocked_schemes: raise ValidationError("Scheme not allowed") # Resolve and check IP import socket try: ip = socket.gethostbyname(parsed.hostname) if is_internal_ip(ip): raise ValidationError("Internal IPs not allowed") except socket.gaierror: raise ValidationError("Could not resolve hostname") return url # Alternative: Use a dedicated SSRF protection library # pip install ssrf-protect from ssrf_protect import SSRFProtect @SSRFProtect() def fetch_url(request): url = request.GET.get('url') # Safe to fetch return requests.get(url) ``` #### API8: Security Misconfiguration > **Risk**: Insecure default configurations expose APIs to attacks **Real-World Impact**: - **2018**: Tesla Kubernetes API exposed (cryptomining) - **2020**: Multiple Elasticsearch instances exposed without auth **Attack Scenarios:** 1. **CORS Misconfiguration** ```http Access-Control-Allow-Origin: * # Too permissive Access-Control-Allow-Credentials: true # Dangerous combination ``` 2. **Verbose Error Messages** ```json { "error": "SQL syntax error near 'user_id = 123'", "stack_trace": "...", "query": "SELECT * FROM users WHERE user_id = 123" } ``` 3. **Missing Security Headers** ```http # Missing headers: X-Content-Type-Options: nosniff X-Frame-Options: DENY Content-Security-Policy: ... Strict-Transport-Security: max-age=31536000 ``` 4. **Debug Mode Enabled** ``` /api/debug/vars # Exposes Go runtime variables /api/swagger-ui/ # API documentation with try-it feature /api/actuator/health # Spring Boot endpoints ``` **Testing Approach:** 1. **Security Headers Check**: ```bash curl -I https://api.example.com/endpoint | grep -i "access-control\|x-" ``` 2. **Error Handling Test**: ```bash # Trigger errors and check responses curl "https://api.example.com/endpoint?param=' OR 1=1--" curl "https://api.example.com/endpoint" -X INVALID_METHOD ``` 3. **Debug Endpoint Discovery**: ```bash # Common debug paths /debug /actuator /swagger-ui /api-docs /health /metrics ``` **Remediation:** ```python # Django middleware for security headers class SecurityHeadersMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): response = self.get_response(request) # Security headers response['X-Content-Type-Options'] = 'nosniff' response['X-Frame-Options'] = 'DENY' response['X-XSS-Protection'] = '1; mode=block' response['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' response['Content-Security-Policy'] = "default-src 'self'" response['Referrer-Policy'] = 'strict-origin-when-cross-origin' # CORS (restrictive) response['Access-Control-Allow-Origin'] = 'https://trusted-domain.com' response['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE' response['Access-Control-Allow-Headers'] = 'Content-Type, Authorization' return response # Disable debug mode in production DEBUG = False # Custom error handler (don't expose internals) from django.http import JsonResponse def custom_error_handler(request, exception=None): return JsonResponse( {"error": "An error occurred"}, status=500 ) ``` #### API9: Improper Inventory Management > **Risk**: Shadow APIs and undocumented endpoints create attack surface **Real-World Impact**: - **2021**: Facebook API exposed user data via undocumented endpoint - **2022**: Twitter API v1.1 endpoints still accessible after v2 launch **Attack Scenarios:** 1. **Shadow API Discovery** ``` Compare OpenAPI spec with actual code Look for endpoints not in documentation ``` 2. **Deprecated API Usage** ```http GET /api/v1/users # Old version still active GET /api/v2/users # New version # v1 may have known vulnerabilities ``` 3. **Environment Confusion** ``` Production API: api.company.com Staging API: api-staging.company.com # Exposed? Dev API: api-dev.company.com # Exposed with debug enabled? ``` **Testing Approach:** 1. **API Inventory**: ```bash # Extract all routes from code grep -r "@app.route\|@GetMapping\|path(" --include="*.py" --include="*.java" # Compare with OpenAPI spec # Identify undocumented endpoints ``` 2. **Version Testing**: ```bash # Test old API versions curl https://api.example.com/v1/users curl https://api.example.com/v2/users ``` 3. **Environment Discovery**: ```bash # Check for exposed environments curl https://api-staging.example.com/health curl https://api-dev.example.com/debug ``` **Remediation:** ```yaml # API Gateway configuration (Kong example) plugins: - name: request-termination config: status_code: 410 message: "API version deprecated" route: api-v1-routes # Automated API discovery # Use tools like: # - OWASP ZAP API scanner # - Postman collection generator # - Swagger Codegen # Regular audits # Monthly: Compare deployed endpoints with documentation # Quarterly: Review and deprecate old API versions ``` #### API10: Unsafe Consumption of APIs > **Risk**: Trusting third-party API responses leads to injection attacks **Real-World Impact**: - **2020**: Multiple XXE attacks via external API responses - **2021**: Deserialization attacks from webhook payloads **Attack Scenarios:** 1. **XXE via External API** ```xml ]> &xxe; ``` 2. **Deserialization Attack** ```python # Attacker controls serialized data import pickle malicious = b'...crafted pickle payload...' data = pickle.loads(malicious) # RCE! ``` 3. **Webhook Replay** ```http # Attacker captures webhook and replays it POST /api/webhooks/payment X-Webhook-Signature: valid_signature {"event": "payment.success", "amount": 1000} # Replay same request to double credits ``` 4. **JSON Injection** ```json // Malicious JSON from external API { "username": "admin", "bio": "" } ``` **Testing Approach:** 1. **XXE Testing**: ```bash # If API accepts XML from third parties # Send malicious XML to test for XXE ``` 2. **Deserialization Testing**: ```bash # Check for unsafe deserialization # Look for pickle, ObjectInputStream, unserialize ``` 3. **Webhook Security**: ```bash # Test webhook replay # Check signature validation # Verify timestamp/nonce checks ``` **Remediation:** ```python # XXE Prevention import xml.etree.ElementTree as ET from defusedxml import ElementTree as DefusedET def parse_xml_safely(xml_string): # Use defusedxml to prevent XXE return DefusedET.fromstring(xml_string) # Deserialization Safety import json def safe_deserialize(data): # Use JSON instead of pickle for untrusted data return json.loads(data) # If you must use pickle, sign the data import hmac import hashlib signature = hmac.new( SECRET_KEY, data, hashlib.sha256 ).hexdigest() if not hmac.compare_digest(signature, expected_signature): raise ValueError("Invalid signature") return pickle.loads(data) # Webhook Security import hmac import hashlib import time def verify_webhook(request): # Verify signature signature = request.headers.get('X-Webhook-Signature') expected = hmac.new( WEBHOOK_SECRET, request.body, hashlib.sha256 ).hexdigest() if not hmac.compare_digest(signature, expected): raise ValueError("Invalid signature") # Check timestamp (prevent replay) timestamp = int(request.headers.get('X-Webhook-Timestamp')) if abs(time.time() - timestamp) > 300: # 5 minutes raise ValueError("Request too old") # Check nonce (prevent replay) nonce = request.headers.get('X-Webhook-Nonce') if nonce in used_nonces: raise ValueError("Nonce already used") used_nonces.add(nonce) return True ``` ### Phase 4: Automated Security Testing #### 4.1 API Security Scanning **OWASP ZAP**: ```bash # Automated API scan zap-api-scan.py -t https://api.example.com/openapi.json -f openapi # With authentication zap-api-scan.py -t https://api.example.com/openapi.json \ -f openapi \ -config "replacer.full_list(0).description=auth" \ -config "replacer.full_list(0).enabled=true" \ -config "replacer.full_list(0).matchtype=REQ_HEADER" \ -config "replacer.full_list(0).matchstr=Authorization" \ -config "replacer.full_list(0).replacement=Bearer $TOKEN" ``` **Burp Suite**: - Import OpenAPI spec - Run active scan on all endpoints - Test for BOLA, authentication bypasses **Postman Security Tests**: ```javascript // Test for sensitive data in response pm.test("Response does not contain sensitive data", function () { pm.expect(pm.response.text()).to.not.include("password"); pm.expect(pm.response.text()).to.not.include("secret"); }); // Test for security headers pm.test("Security headers present", function () { pm.response.to.have.header("X-Content-Type-Options"); pm.response.to.have.header("X-Frame-Options"); }); ``` #### 4.2 Custom Security Tests **BOLA Testing Script**: ```python import requests def test_bola(base_url, token, user_a_id, user_b_id): """Test for Broken Object Level Authorization""" # Get User A's token headers = {"Authorization": f"Bearer {token}"} # Try to access User B's resources endpoints = [ f"/api/users/{user_b_id}", f"/api/users/{user_b_id}/orders", f"/api/orders?user_id={user_b_id}", ] for endpoint in endpoints: response = requests.get(f"{base_url}{endpoint}", headers=headers) if response.status_code == 200: print(f"BOLA VULNERABILITY: {endpoint}") print(f"User A can access User B's data!") ``` **Rate Limit Testing**: ```python import requests import time def test_rate_limiting(url, headers, requests_count=100): """Test if rate limiting is enforced""" success_count = 0 start_time = time.time() for i in range(requests_count): response = requests.get(url, headers=headers) if response.status_code == 200: success_count += 1 elif response.status_code == 429: print(f"Rate limit hit after {i} requests") break elapsed = time.time() - start_time print(f"Made {success_count} requests in {elapsed:.2f} seconds") if success_count == requests_count: print("WARNING: No rate limiting detected!") ``` ### Phase 5: API Gateway & Infrastructure Review #### 5.1 Kong Gateway Configuration ```yaml # kong.yml plugins: # Rate limiting - name: rate-limiting config: minute: 100 hour: 1000 policy: redis redis_host: redis # CORS (restrictive) - name: cors config: origins: - "https://app.example.com" methods: - GET - POST - PUT - DELETE headers: - Authorization - Content-Type credentials: true max_age: 3600 # Request size limiting - name: request-size-limiting config: allowed_payload_size: 10 # MB # Bot detection - name: bot-detection config: deny: - "Mozilla/5.0 (compatible; Googlebot/2.1)" # IP restriction - name: ip-restriction config: allow: - "10.0.0.0/8" - "172.16.0.0/12" ``` #### 5.2 nginx Configuration ```nginx server { listen 443 ssl http2; server_name api.example.com; # SSL configuration ssl_certificate /path/to/cert.pem; ssl_certificate_key /path/to/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'; # Security headers add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; # Rate limiting limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; limit_req zone=api burst=20 nodelay; # Request size limit client_max_body_size 10M; # Timeout settings proxy_connect_timeout 30s; proxy_send_timeout 30s; proxy_read_timeout 30s; location / { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } ``` #### 5.3 AWS API Gateway ```yaml # serverless.yml or CloudFormation Resources: ApiGateway: Type: AWS::ApiGateway::RestApi Properties: Name: SecureAPI # Throttling ApiGatewayStage: Type: AWS::ApiGateway::Stage Properties: RestApiId: !Ref ApiGateway StageName: prod MethodSettings: - ResourcePath: /* HttpMethod: * ThrottlingBurstLimit: 100 ThrottlingRateLimit: 50 # WAF WebACL: Type: AWS::WAFv2::WebACL Properties: Name: APIWAF Rules: - Name: RateLimit Priority: 1 Statement: RateBasedStatement: Limit: 2000 AggregateKeyType: IP Action: Block: {} VisibilityConfig: SampledRequestsEnabled: true ``` ## Output Format ```markdown ## API Security Assessment: [API Name] ### Executive Summary - **API Type**: REST | GraphQL | gRPC | SOAP - **Total Endpoints**: [count] - **Authentication**: [JWT | OAuth 2.0 | API Keys | Session] - **Risk Rating**: [Critical | High | Medium | Low] - **Overall Security Posture**: [Brief assessment] ### API Surface Inventory #### Authentication Mechanisms | Mechanism | Implementation | Security Rating | |-----------|---------------|-----------------| | JWT | HS256, 15min expiry | ✅ Secure | | OAuth 2.0 | PKCE enabled | ✅ Secure | #### Endpoint Breakdown | Category | Count | Auth Required | |----------|-------|---------------| | Public | 5 | No | | Authenticated | 45 | Yes | | Admin | 8 | Yes + Admin role | ### Risk Matrix | OWASP API Ref | Risk | Severity | Status | Evidence | |--------------|------|----------|--------|----------| | API1 | BOLA | HIGH | Finding | /api/orders/{id} lacks ownership check | | API2 | Broken Auth | MEDIUM | Finding | JWT alg=none accepted | | API3 | BOPA | HIGH | Finding | Mass assignment on /api/users | | API4 | Resource Consumption | LOW | Finding | No rate limiting on search | | API5 | BFLA | MEDIUM | Finding | Admin endpoints accessible to users | | API6 | Business Flows | N/A | N/A | Not applicable | | API7 | SSRF | CRITICAL | Finding | Cloud metadata accessible | | API8 | Misconfiguration | MEDIUM | Finding | Debug mode enabled | | API9 | Inventory | LOW | Finding | Undocumented /api/internal endpoints | | API10 | Unsafe Consumption | HIGH | Finding | Webhooks lack signature validation | ### Critical Findings #### [CRITICAL] API7: SSRF - Cloud Metadata Exposure - **Location**: `/api/fetch?url=` parameter - **Impact**: AWS credentials can be extracted, leading to full cloud compromise - **Evidence**: ``` GET /api/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ Response: 200 OK with IAM role credentials ``` - **Remediation**: Implement URL allowlist, block internal IPs ### High Findings [Additional findings...] ### Positive Security Controls - ✅ JWT tokens use strong signing (RS256) - ✅ HTTPS enforced with TLS 1.2+ - ✅ Security headers present - ✅ Input validation on all endpoints ### Remediation Roadmap #### Immediate (Critical/High - 1 week) 1. Fix SSRF vulnerability (API7) 2. Implement BOLA checks on all object endpoints (API1) 3. Disable debug mode (API8) #### Short-term (High/Medium - 1 month) 4. Implement comprehensive rate limiting (API4) 5. Add field-level authorization (API3) 6. Secure admin endpoints (API5) #### Long-term (Medium/Low - 3 months) 7. Implement API inventory management (API9) 8. Add webhook signature validation (API10) 9. Conduct regular security testing ### Testing Artifacts - [ ] OWASP ZAP scan results - [ ] Burp Suite scan results - [ ] Custom test scripts output - [ ] API gateway configuration review ### References - OWASP API Security Top 10 (2023) - OWASP ASVS v5.0 - V13: API and Web Service - OWASP Testing Guide: WSTG-APIT ``` ## OWASP References - **OWASP API Security Top 10 (2023)** — [owasp.org/API-Security](https://owasp.org/API-Security/) - **OWASP ASVS v5.0** — V13: API and Web Service Verification - **OWASP Testing Guide** — WSTG-APIT: API Testing - **OWASP Cheat Sheet Series**: - REST Security - GraphQL Security - JWT Security - OAuth 2.0 - SAML Security - **OWASP ZAP** — API scanning capabilities