> "APIs are the new perimeter. When you expose an API, you're exposing your business logic directly to the internet." — OWASP API Security Project
Learning Objectives
- Understand REST, GraphQL, and gRPC security architectures
- Perform API reconnaissance and documentation discovery
- Identify and exploit Broken Object Level Authorization (BOLA/IDOR)
- Test for mass assignment and excessive data exposure vulnerabilities
- Assess rate limiting, resource exhaustion, and business logic flaws
- Attack API authentication and token management
- Apply the OWASP API Security Top 10 framework systematically
In This Chapter
- Introduction
- 23.1 API Architectures and Security Models
- 23.2 API Reconnaissance and Documentation Discovery
- 23.3 Broken Object Level Authorization (BOLA/IDOR)
- 23.4 Mass Assignment and Excessive Data Exposure
- 23.5 Rate Limiting, Resource Exhaustion, and Business Logic
- 23.6 API Authentication and Token Attacks
- 23.7 OWASP API Security Top 10 (2023)
- 23.8 Practical Lab: Full ShopStack API Assessment
- 23.9 MedSecure API Security Considerations
- 23.10 Building an API Testing Lab
- 23.11 API Security Testing Automation
- 23.12 Summary
- Key Terms
Chapter 23: API Security Testing
"APIs are the new perimeter. When you expose an API, you're exposing your business logic directly to the internet." — OWASP API Security Project
Introduction
In September 2022, Australian telecommunications giant Optus suffered one of the country's worst data breaches when an attacker discovered an unauthenticated API endpoint that exposed the personal records of 9.8 million customers — approximately 40% of the Australian population. The endpoint required no authentication, no authorization, and returned customer data including names, dates of birth, phone numbers, email addresses, and in some cases passport and driver's license numbers. The attacker simply enumerated sequential customer identifiers to harvest the entire database.
The Optus breach was not an isolated incident. T-Mobile has experienced at least eight significant API-related breaches since 2018, each exposing millions of customer records through similar vulnerabilities: insufficient authentication, broken authorization, and excessive data exposure. Peloton's API exposed private user data to any unauthenticated request, allowing anyone to query user profiles, workout statistics, and location data regardless of privacy settings.
These breaches reflect a fundamental shift in the attack landscape. As organizations adopt API-first architectures, microservices, and cloud-native designs, APIs have become the primary attack surface. Unlike traditional web applications where business logic is mediated through a user interface, APIs expose that logic directly — creating opportunities for attackers to interact with backend systems in ways the developers never anticipated.
This chapter provides a comprehensive methodology for API security testing. We cover the three dominant API paradigms (REST, GraphQL, and gRPC), systematic reconnaissance techniques, and the full spectrum of API-specific vulnerabilities as cataloged by the OWASP API Security Top 10. Throughout, we test these techniques against ShopStack's API, which serves as a realistic target with REST endpoints, a GraphQL interface for the storefront, and gRPC for internal microservice communication.
⚠️ Legal and Ethical Notice: API testing can generate high volumes of requests and may affect service availability. Always coordinate with system owners, establish rate limits for your testing tools, and have monitoring dashboards open during active testing. Never test APIs in production environments without explicit authorization.
23.1 API Architectures and Security Models
Before testing APIs, you must understand their architectural patterns, as each carries distinct security implications.
23.1.1 REST API Security
Representational State Transfer (REST) APIs use HTTP methods and URL paths to represent resources:
GET /api/v1/products # List products
POST /api/v1/products # Create product
GET /api/v1/products/123 # Read product 123
PUT /api/v1/products/123 # Update product 123
DELETE /api/v1/products/123 # Delete product 123
Security Characteristics of REST APIs: - Stateless: Each request must contain all information needed for authentication and authorization. Session state is not maintained server-side (in pure REST). - Resource-Oriented: Authorization must be enforced per resource and per method. A user authorized to GET a resource may not be authorized to DELETE it. - URL-Based Resources: Resource identifiers in URLs create opportunities for enumeration and IDOR attacks. - Content Negotiation: APIs accepting multiple content types (JSON, XML, YAML) expand the attack surface.
ShopStack's primary API is REST-based:
# Public endpoints (no authentication)
GET /api/v1/products
GET /api/v1/products/{id}
GET /api/v1/categories
# Authenticated endpoints (customer)
GET /api/v1/orders
POST /api/v1/orders
GET /api/v1/profile
# Authenticated endpoints (merchant)
POST /api/v1/merchant/products
GET /api/v1/merchant/analytics
# Administrative endpoints
GET /api/v1/admin/users
POST /api/v1/admin/config
23.1.2 GraphQL Security
GraphQL provides a query language for APIs, allowing clients to request exactly the data they need:
query {
product(id: "123") {
name
price
reviews {
author {
name
email # Should this be exposed?
}
rating
text
}
}
}
Security Characteristics of GraphQL:
- Single Endpoint: Typically /graphql, making traditional URL-based security rules ineffective.
- Client-Controlled Queries: Clients determine what data to fetch, creating excessive data exposure risks if the schema exposes sensitive fields.
- Introspection: GraphQL's built-in schema introspection system can reveal the entire API surface area.
- Nested Queries: Deeply nested queries can cause denial of service through resource exhaustion.
- Batched Operations: Multiple queries in a single request can bypass rate limiting.
- Mutations: Write operations can be embedded alongside read operations.
ShopStack's storefront uses GraphQL:
POST /graphql HTTP/1.1
Host: shopstack.local
Content-Type: application/json
{
"query": "query { products(first: 10) { edges { node { id name price merchant { name email apiKey } } } } }"
}
23.1.3 gRPC Security
gRPC uses Protocol Buffers for serialization and HTTP/2 for transport:
// ShopStack internal service definition
service OrderService {
rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
rpc GetOrder(GetOrderRequest) returns (Order);
rpc ListOrders(ListOrdersRequest) returns (stream Order);
}
message CreateOrderRequest {
string customer_id = 1;
repeated OrderItem items = 2;
PaymentInfo payment = 3;
}
Security Characteristics of gRPC: - Binary Protocol: Protocol Buffers are binary-encoded, making manual inspection difficult but not impossible. - HTTP/2 Transport: Multiplexed streams, header compression, and server push create unique attack surfaces. - Strong Typing: Schema enforcement reduces some injection attacks but doesn't prevent authorization flaws. - Reflection API: Like GraphQL introspection, gRPC reflection reveals service definitions. - Interceptors: Authentication/authorization is typically implemented through interceptors (middleware).
# Test gRPC reflection (if enabled)
grpcurl -plaintext shopstack-internal.local:50051 list
# Call a service
grpcurl -plaintext -d '{"customer_id": "12345"}' \
shopstack-internal.local:50051 shopstack.OrderService/ListOrders
💡 Practical Tip: Many organizations secure their REST APIs but leave internal gRPC services unprotected, assuming network segmentation is sufficient. During your ShopStack assessment, check whether gRPC services are accessible from the application server (via SSRF) or from compromised containers.
23.2 API Reconnaissance and Documentation Discovery
Effective API testing begins with comprehensive reconnaissance. Unlike traditional web applications where you can crawl the interface, APIs require different discovery techniques.
23.2.1 Passive Reconnaissance
API Documentation Discovery:
# Common documentation endpoints
curl -s https://shopstack.local/api/docs
curl -s https://shopstack.local/api/v1/docs
curl -s https://shopstack.local/swagger.json
curl -s https://shopstack.local/swagger/v1/swagger.json
curl -s https://shopstack.local/api-docs
curl -s https://shopstack.local/openapi.json
curl -s https://shopstack.local/openapi.yaml
curl -s https://shopstack.local/api/v1/openapi.json
curl -s https://shopstack.local/v1/api-docs
curl -s https://shopstack.local/graphql # GraphQL playground
curl -s https://shopstack.local/.well-known/openapi.json
# Swagger UI common paths
curl -s https://shopstack.local/swagger-ui.html
curl -s https://shopstack.local/swagger-ui/
curl -s https://shopstack.local/api/swagger-ui.html
# Redoc documentation
curl -s https://shopstack.local/redoc
JavaScript Analysis: Single-page applications (SPAs) that consume APIs often contain API endpoint information in their JavaScript bundles:
# Download and search JavaScript files
curl -s https://shopstack.local/static/js/main.bundle.js | \
grep -oE '/api/v[0-9]+/[a-zA-Z/\-_]+'
# Look for API base URLs
curl -s https://shopstack.local/static/js/main.bundle.js | \
grep -oE 'https?://[a-zA-Z0-9\.\-]+/api/[a-zA-Z/\-_]*'
Historical Data:
# Wayback Machine API discovery
curl -s "http://web.archive.org/cdx/search/cdx?url=shopstack.local/api/*&output=text&fl=original" | sort -u
# Search for API keys and documentation in code repositories
# GitHub dork: "shopstack.local" api_key OR apikey OR secret
23.2.2 Active Reconnaissance
Endpoint Enumeration with Kiterunner:
# Kiterunner is purpose-built for API endpoint discovery
kr scan https://shopstack.local -w routes-large.kite -x 20
# Test with common API wordlists
kr brute https://shopstack.local/api/v1/ -w api-wordlist.txt
Endpoint Enumeration with ffuf:
# Fuzz API endpoint paths
ffuf -u https://shopstack.local/api/v1/FUZZ -w api-endpoints.txt \
-mc 200,201,301,302,401,403,405 -t 10
# Fuzz API versions
ffuf -u https://shopstack.local/api/vFUZZ/users -w <(seq 1 10) \
-mc 200,201,301,302,401,403
# Fuzz with different HTTP methods
for method in GET POST PUT DELETE PATCH OPTIONS; do
ffuf -u https://shopstack.local/api/v1/admin/users -X $method \
-w /dev/null -mc 200,201,204,401,403,405
done
GraphQL Introspection:
# Full introspection query
{
__schema {
types {
name
fields {
name
type {
name
kind
ofType {
name
}
}
args {
name
type {
name
}
}
}
}
queryType { name }
mutationType { name }
subscriptionType { name }
}
}
If introspection is enabled, this reveals the entire schema — every type, field, query, mutation, and subscription the API supports. Feed this into GraphQL Voyager to generate an interactive schema visualization.
gRPC Reflection:
# List all services
grpcurl -plaintext shopstack-internal.local:50051 list
# Describe a service
grpcurl -plaintext shopstack-internal.local:50051 \
describe shopstack.OrderService
# Describe a message type
grpcurl -plaintext shopstack-internal.local:50051 \
describe shopstack.CreateOrderRequest
23.2.3 API Parameter Discovery
Parameter Fuzzing with Arjun:
# Discover hidden parameters
arjun -u https://shopstack.local/api/v1/products -m GET
arjun -u https://shopstack.local/api/v1/products -m POST --json
# Output:
# [+] Found parameters: sort, filter, fields, include, debug, verbose, admin
Content-Type Testing: Test whether the API accepts different content types, which may reveal additional processing paths:
# Standard JSON
POST /api/v1/products HTTP/1.1
Content-Type: application/json
{"name": "test"}
# XML (may enable XXE)
POST /api/v1/products HTTP/1.1
Content-Type: application/xml
<product><name>test</name></product>
# URL-encoded (may bypass JSON-specific validation)
POST /api/v1/products HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=test
📊 Industry Data: A 2024 study by Salt Security found that API attacks increased 681% year-over-year, with 94% of organizations experiencing API security incidents. The average organization exposes 135 APIs, but security teams are typically aware of only 60% of them — a phenomenon known as "shadow APIs."
23.3 Broken Object Level Authorization (BOLA/IDOR)
BOLA (also known as IDOR — Insecure Direct Object Reference) is the #1 vulnerability in the OWASP API Security Top 10. It occurs when an API endpoint exposes object identifiers and fails to verify that the requesting user is authorized to access the specified object.
23.3.1 Understanding BOLA
ShopStack's API includes numerous endpoints that accept object identifiers:
GET /api/v1/orders/10001 # View order details
GET /api/v1/users/500/profile # View user profile
GET /api/v1/invoices/INV-2024-001 # View invoice
GET /api/v1/messages/msg_abc123 # View message
If user Alice (who owns order 10001) can access order 10002 (which belongs to Bob) simply by changing the identifier, the API has a BOLA vulnerability.
23.3.2 Testing for BOLA
Methodology:
- Create two test accounts with different privilege levels
- Authenticate as User A and capture requests for User A's resources
- Replay those requests using User B's credentials with User A's resource IDs
- If User B can access User A's resources, BOLA is confirmed
# Authenticated as user alice (order owner)
GET /api/v1/orders/10001 HTTP/1.1
Authorization: Bearer <alice_token>
# Response: 200 OK with order details
# Replay with bob's token
GET /api/v1/orders/10001 HTTP/1.1
Authorization: Bearer <bob_token>
# Vulnerable: 200 OK with alice's order details
# Secure: 403 Forbidden
Testing Different HTTP Methods: A common mistake is implementing authorization for GET requests but not for PUT/DELETE:
# Read may be protected
GET /api/v1/orders/10001
Authorization: Bearer <bob_token>
# 403 Forbidden - Good
# But modification may not be
PUT /api/v1/orders/10001
Authorization: Bearer <bob_token>
Content-Type: application/json
{"status": "cancelled"}
# 200 OK - BOLA on write operations!
23.3.3 Identifier Patterns and Enumeration
Different identifier patterns require different enumeration strategies:
Sequential Integers: The simplest to enumerate
/api/v1/users/1
/api/v1/users/2
/api/v1/users/3
UUIDs: Appear random but may be discoverable through other endpoints
/api/v1/users/550e8400-e29b-41d4-a716-446655440000
UUIDs can be found in: API responses (listing endpoints), WebSocket messages, email links, HTML source code, JavaScript variables, push notifications.
Encoded Identifiers: May appear opaque but decode to simple values
/api/v1/users/NTAw # Base64 of "500"
/api/v1/users/1f3870be274f6c49b3e31a0c6728957f # MD5 of "500"
Composite Identifiers:
/api/v1/orders/2024-01-15-10001 # Date + sequential
/api/v1/invoices/INV-ACME-001 # Prefix + company + sequential
23.3.4 BOLA in GraphQL
GraphQL BOLA requires different testing approaches because resources are accessed through queries, not URL paths:
# Test BOLA in GraphQL
query {
order(id: "10001") { # Alice's order
id
total
items { name quantity }
customer {
name
email
address # Sensitive data
}
}
}
Even if the top-level query checks authorization, nested relationships may not:
# Access another user's data through a relationship
query {
myReview(id: "review_123") {
product {
reviews {
author {
orders { # Access other users' orders through review->product->reviews->author->orders chain
total
shippingAddress
}
}
}
}
}
}
🔵 Blue Team Perspective: Prevent BOLA by (1) implementing authorization checks on every endpoint that accesses objects by ID, (2) using the authenticated user's identity from the session/token to scope data access (e.g.,
SELECT * FROM orders WHERE user_id = :authenticated_user AND id = :requested_id), (3) preferring UUIDs over sequential integers to reduce enumerability (note: this is defense-in-depth, not a primary control), (4) implementing logging and anomaly detection for excessive object access patterns.
23.4 Mass Assignment and Excessive Data Exposure
23.4.1 Mass Assignment
Mass assignment (also called auto-binding) occurs when an API automatically binds client-provided data to internal object properties without filtering. If the internal object contains privileged fields, an attacker can modify them.
Testing ShopStack for Mass Assignment:
# Normal profile update
PUT /api/v1/profile HTTP/1.1
Authorization: Bearer <user_token>
Content-Type: application/json
{
"name": "Alice Smith",
"email": "alice@example.com"
}
# Mass assignment attempt - add privileged fields
PUT /api/v1/profile HTTP/1.1
Authorization: Bearer <user_token>
Content-Type: application/json
{
"name": "Alice Smith",
"email": "alice@example.com",
"role": "admin",
"is_verified": true,
"account_balance": 99999.99,
"discount_percentage": 100,
"subscription_tier": "enterprise",
"is_staff": true
}
Discovery Techniques:
- Read API documentation for internal field names
- Examine API responses for fields not present in input forms
- Use GraphQL introspection to discover all fields on a type
- Try adding common privileged field names: role, admin, verified, active, permissions, group, level, tier
Mass Assignment in Account Registration:
POST /api/v1/auth/register HTTP/1.1
Content-Type: application/json
{
"username": "attacker",
"password": "password123",
"email": "attacker@example.com",
"role": "admin",
"is_verified": true
}
23.4.2 Excessive Data Exposure
APIs often return more data than the client needs, relying on the frontend to filter sensitive fields. This is a particularly common issue with GraphQL APIs that lack field-level authorization.
Testing for Excessive Data Exposure:
# Request a product listing
GET /api/v1/products/123 HTTP/1.1
Authorization: Bearer <user_token>
# Response may include internal fields
{
"id": 123,
"name": "Widget Pro",
"price": 29.99,
"cost": 12.50, # Internal cost data
"supplier_id": "SUP-001", # Internal supplier reference
"margin": 0.58, # Profit margin
"merchant": {
"name": "Acme Corp",
"email": "merchant@acme.com",
"api_key": "sk_live_abc123", # API key exposed!
"bank_account": "****4567"
},
"internal_notes": "Discontinuing Q3 2024",
"warehouse_location": "Bin A-42"
}
GraphQL Excessive Data Exposure: GraphQL is particularly susceptible because clients can request any field defined in the schema:
# Introspect to find all fields on User type
{
__type(name: "User") {
fields {
name
type { name }
}
}
}
# Result reveals sensitive fields
# name, email, role, passwordHash, ssn, creditScore, internalId
Then request the sensitive fields:
query {
users {
name
email
passwordHash # Sensitive!
ssn # PII!
creditScore # Sensitive!
internalId
}
}
🔵 Blue Team Perspective: Prevent mass assignment by (1) explicitly defining which fields are bindable from user input (allowlisting), (2) using separate DTOs (Data Transfer Objects) for input and output, (3) never directly binding request data to database models. Prevent excessive data exposure by (4) implementing field-level authorization, (5) reviewing API responses to ensure no sensitive data leaks, (6) using GraphQL field-level directives like
@authto enforce access control on sensitive fields, (7) implementing response filtering at the serialization layer.
23.5 Rate Limiting, Resource Exhaustion, and Business Logic
23.5.1 Rate Limiting Assessment
APIs without proper rate limiting are vulnerable to brute force, enumeration, denial of service, and data harvesting. Test ShopStack's rate limiting across all endpoint categories:
# Test rate limiting on authentication endpoint
for i in $(seq 1 100); do
curl -s -o /dev/null -w "%{http_code}" \
-X POST https://shopstack.local/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"attempt'$i'"}'
echo " - Attempt $i"
done
# Expected: 429 Too Many Requests after N attempts
# Vulnerable: 200/401 responses continue indefinitely
Rate Limiting Bypass Techniques:
# IP rotation headers
X-Forwarded-For: 1.2.3.4
X-Real-IP: 1.2.3.5
X-Originating-IP: 1.2.3.6
X-Client-IP: 1.2.3.7
CF-Connecting-IP: 1.2.3.8
True-Client-IP: 1.2.3.9
# Case variation on endpoints
/api/v1/auth/login
/api/v1/auth/Login
/api/v1/AUTH/LOGIN
/Api/V1/Auth/Login
# Parameter variation
/api/v1/auth/login?dummy=1
/api/v1/auth/login#anchor
/api/v1/auth/login/
# HTTP method variation
POST /api/v1/auth/login
PUT /api/v1/auth/login
# API version variation
/api/v1/auth/login
/api/v2/auth/login
/api/v1.0/auth/login
23.5.2 Resource Exhaustion
GraphQL Query Complexity Attacks:
GraphQL's flexibility makes it particularly vulnerable to resource exhaustion through deeply nested or highly connected queries:
# Deeply nested query (GraphQL bomb)
query {
products(first: 100) {
reviews(first: 100) {
author {
orders(first: 100) {
items(first: 100) {
product {
reviews(first: 100) {
author {
orders(first: 100) {
# ... continues nesting
}
}
}
}
}
}
}
}
}
}
This query could result in billions of database queries, causing denial of service.
Batch Query Abuse:
// GraphQL batching - send 1000 operations in one request
[
{"query": "mutation { login(user: \"admin\", pass: \"password1\") { token } }"},
{"query": "mutation { login(user: \"admin\", pass: \"password2\") { token } }"},
{"query": "mutation { login(user: \"admin\", pass: \"password3\") { token } }"}
// ... 997 more
]
This bypasses rate limiting that counts HTTP requests rather than operations.
Pagination Abuse:
# Request excessive page sizes
GET /api/v1/products?page=1&per_page=1000000
# Negative or zero pagination
GET /api/v1/products?page=-1&per_page=0
# Request all records
GET /api/v1/products?limit=999999999
23.5.3 Business Logic Vulnerabilities
API business logic flaws are application-specific and cannot be detected by automated scanners. They require understanding of the intended business workflow.
Race Conditions:
import requests
import concurrent.futures
# Apply a discount code multiple times simultaneously
def apply_discount():
return requests.post(
"https://shopstack.local/api/v1/cart/apply-coupon",
json={"code": "SAVE20"},
headers={"Authorization": f"Bearer {token}"}
)
# Send 50 simultaneous requests
with concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
results = list(executor.map(lambda _: apply_discount(), range(50)))
# Check if the discount was applied multiple times
cart = requests.get(
"https://shopstack.local/api/v1/cart",
headers={"Authorization": f"Bearer {token}"}
).json()
print(f"Total discount: {cart['discount']}") # Expected: 20%, Got: 400%?
Price Manipulation:
# Modify price in API request
POST /api/v1/orders HTTP/1.1
Content-Type: application/json
{
"items": [
{"product_id": "123", "quantity": 1, "price": 0.01}
],
"payment_method": "credit_card"
}
Does the server validate prices against the product catalog, or does it trust client-provided prices?
Workflow Bypass:
# Skip payment step by directly calling order confirmation
POST /api/v1/orders/confirm HTTP/1.1
Content-Type: application/json
{"order_id": "ORD-2024-001"}
# Does the API verify that payment was actually processed?
Negative Quantity/Amount:
POST /api/v1/cart/items HTTP/1.1
Content-Type: application/json
{"product_id": "123", "quantity": -5}
# Does this create a credit instead of a charge?
🧪 Lab Exercise: Map ShopStack's complete checkout workflow (add to cart -> apply coupon -> enter shipping -> process payment -> confirm order). Identify at least three points where the workflow could be manipulated through the API by skipping steps, reordering operations, or providing unexpected values.
23.6 API Authentication and Token Attacks
23.6.1 Authentication Mechanism Assessment
Evaluate all authentication mechanisms used by ShopStack's API:
# API Key authentication (merchant API)
GET /api/v1/merchant/orders HTTP/1.1
X-API-Key: sk_live_abc123
# Bearer token authentication (customer API)
GET /api/v1/orders HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
# Basic authentication (legacy admin API)
GET /api/v1/admin/status HTTP/1.1
Authorization: Basic YWRtaW46cGFzc3dvcmQ=
# OAuth 2.0 (third-party integrations)
GET /api/v1/data HTTP/1.1
Authorization: Bearer oauth_access_token_here
Testing Authentication Bypass:
# Remove authentication header entirely
GET /api/v1/orders HTTP/1.1
# No Authorization header
# Empty authentication
Authorization: Bearer
Authorization: Bearer null
Authorization: Bearer undefined
Authorization: Bearer ""
# Invalid token format
Authorization: Bearer invalid
Authorization: Bearer aaaa
# Old/expired tokens
Authorization: Bearer <previously_valid_token>
# Token from different environment
Authorization: Bearer <staging_token_on_production>
23.6.2 API Key Security
# Test API key in different locations
GET /api/v1/merchant/orders?api_key=sk_live_abc123 # Query parameter
GET /api/v1/merchant/orders HTTP/1.1
X-API-Key: sk_live_abc123 # Custom header
Authorization: Bearer sk_live_abc123 # Auth header
# Test key scope violations
# If key is for read-only:
DELETE /api/v1/merchant/products/123
X-API-Key: sk_live_readonly_key
# Test key after user deactivation
# Deactivate merchant account, then attempt API access
GET /api/v1/merchant/orders
X-API-Key: sk_live_deactivated_merchant
23.6.3 Token Lifecycle Attacks
Token Expiration Testing:
# Obtain a token and test at various intervals
TOKEN=$(curl -s -X POST https://shopstack.local/api/v1/auth/login \
-d '{"username":"test","password":"test123"}' | jq -r '.token')
# Test immediately
curl -s -H "Authorization: Bearer $TOKEN" https://shopstack.local/api/v1/profile
# Wait for supposed expiration (e.g., 1 hour)
# Then test again
sleep 3601
curl -s -H "Authorization: Bearer $TOKEN" https://shopstack.local/api/v1/profile
# Should return 401 if token properly expired
Token Revocation Testing:
# Obtain token, then change password or logout
# Test whether the old token is still valid
# Step 1: Login and get token
TOKEN=$(curl -s -X POST .../login -d '{"username":"test","password":"test123"}' | jq -r '.token')
# Step 2: Change password
curl -s -X POST .../change-password \
-H "Authorization: Bearer $TOKEN" \
-d '{"old_password":"test123","new_password":"newpass456"}'
# Step 3: Test old token - should be revoked
curl -s -H "Authorization: Bearer $TOKEN" .../profile
# Vulnerable if still returns 200
Refresh Token Attacks:
# Test refresh token reuse after rotation
POST /api/v1/auth/refresh HTTP/1.1
Content-Type: application/json
{"refresh_token": "old_refresh_token"}
# If old refresh tokens still work after new ones are issued,
# a stolen refresh token provides indefinite access
🔵 Blue Team Perspective: Secure API authentication by (1) using short-lived access tokens (15-30 minutes) with refresh token rotation, (2) implementing token revocation (blacklisting or using a token registry), (3) binding tokens to client fingerprints where possible, (4) transmitting API keys only in headers (never in URLs, which appear in logs), (5) implementing API key rotation procedures and auditing key usage, and (6) using mTLS for service-to-service authentication.
23.7 OWASP API Security Top 10 (2023)
The OWASP API Security Top 10 provides a comprehensive framework for API security assessment. Let's map each item to our ShopStack testing methodology.
API1:2023 — Broken Object Level Authorization
Covered in Section 23.3. The #1 API vulnerability. Test every endpoint that accepts object identifiers with cross-user authorization checks.
API2:2023 — Broken Authentication
Covered in Section 23.6 and Chapter 21. Test for weak authentication, missing authentication on sensitive endpoints, and token management flaws.
API3:2023 — Broken Object Property Level Authorization
Combines mass assignment and excessive data exposure (Section 23.4). Test whether users can read or write object properties they shouldn't have access to.
API4:2023 — Unrestricted Resource Consumption
Covered in Section 23.5. Test for missing rate limiting, pagination limits, and query complexity controls.
API5:2023 — Broken Function Level Authorization
Similar to BOLA but at the function/endpoint level. Test whether regular users can access administrative endpoints:
# As a regular customer, attempt admin operations
DELETE /api/v1/admin/users/500 HTTP/1.1
Authorization: Bearer <customer_token>
POST /api/v1/admin/config HTTP/1.1
Authorization: Bearer <customer_token>
Content-Type: application/json
{"maintenance_mode": true}
# Test by changing HTTP method
GET /api/v1/admin/users # May return 403
POST /api/v1/admin/users # May be unprotected
API6:2023 — Unrestricted Access to Sensitive Business Flows
Identifies business flows that can be abused through automation:
# Automated coupon creation/testing
POST /api/v1/coupons/validate
{"code": "AAAA"}
{"code": "AAAB"}
# ... enumerate until valid code found
# Automated reservation/scalping
POST /api/v1/events/tickets/reserve
# Bot can reserve all tickets before humans
# Automated account creation for spam
POST /api/v1/auth/register
# No CAPTCHA or rate limiting
API7:2023 — Server Side Request Forgery
Covered in Chapter 22. Test any API endpoint that accepts URLs as input.
API8:2023 — Security Misconfiguration
# Check for verbose error messages
GET /api/v1/products/nonexistent HTTP/1.1
# Response may include stack traces, framework versions, internal paths
# Check for unnecessary HTTP methods
OPTIONS /api/v1/products HTTP/1.1
# Response: Allow: GET, POST, PUT, DELETE, TRACE, OPTIONS
# Check CORS configuration
curl -H "Origin: https://attacker.com" -I https://shopstack.local/api/v1/products
# Vulnerable: Access-Control-Allow-Origin: https://attacker.com
# Or worse: Access-Control-Allow-Origin: *
# Check for debug endpoints
GET /api/v1/debug HTTP/1.1
GET /api/v1/status HTTP/1.1
GET /api/v1/health HTTP/1.1
GET /api/v1/metrics HTTP/1.1
GET /api/v1/env HTTP/1.1
# Check TLS configuration
nmap --script ssl-enum-ciphers -p 443 shopstack.local
API9:2023 — Improper Inventory Management
Test for shadow APIs, deprecated versions, and undocumented endpoints:
# Test deprecated API versions
for v in 1 2 3 4 5; do
status=$(curl -s -o /dev/null -w "%{http_code}" \
https://shopstack.local/api/v$v/products)
echo "v$v: $status"
done
# v1: 200 (old, deprecated, potentially less secure)
# v2: 200 (current)
# v3: 200 (beta, undocumented)
# v4: 404
# v5: 404
# Test internal/staging endpoints
curl -s https://shopstack.local/api/internal/debug
curl -s https://shopstack.local/api/staging/users
curl -s https://shopstack-staging.local/api/v1/users
API10:2023 — Unsafe Consumption of APIs
Test how ShopStack's API handles data from third-party APIs:
# If ShopStack consumes a payment gateway webhook
POST /api/v1/webhooks/payment-gateway HTTP/1.1
Content-Type: application/json
{
"event": "payment.completed",
"order_id": "ORD-2024-001",
"amount": 0.01, # Modified amount
"status": "success"
}
# Does ShopStack validate this against the actual payment?
# Does it verify the webhook signature?
📊 Assessment Checklist: Use this OWASP API Security Top 10 mapping as a checklist for your ShopStack API assessment. Each item should be tested across all API paradigms (REST, GraphQL, gRPC) and all user roles (unauthenticated, customer, merchant, admin).
23.8 Practical Lab: Full ShopStack API Assessment
Phase 1: Reconnaissance (30 minutes)
# 1. Discover API documentation
# 2. Enumerate endpoints with Kiterunner/ffuf
# 3. GraphQL introspection
# 4. JavaScript analysis for hidden endpoints
# 5. Map all authentication mechanisms
Phase 2: Authentication Testing (45 minutes)
# 1. Test each authentication mechanism for bypass
# 2. Test token lifecycle (expiration, revocation, rotation)
# 3. Test API key scope and permission boundaries
# 4. Test for broken function level authorization
Phase 3: Authorization Testing (60 minutes)
# 1. BOLA: Test every endpoint with cross-user identifiers
# 2. Test with different privilege levels (customer/merchant/admin)
# 3. Test horizontal privilege escalation (user A -> user B)
# 4. Test vertical privilege escalation (user -> admin)
Phase 4: Data Exposure Testing (30 minutes)
# 1. Review all API responses for sensitive data
# 2. Test mass assignment on create/update endpoints
# 3. GraphQL field-level authorization
# 4. Test error message information disclosure
Phase 5: Business Logic Testing (45 minutes)
# 1. Map business workflows and test for bypasses
# 2. Test race conditions on critical operations
# 3. Test rate limiting and resource exhaustion
# 4. Test pagination limits and query complexity
Phase 6: Injection Testing (30 minutes)
# 1. SSRF on URL parameters
# 2. SQL injection on filter/search parameters
# 3. NoSQL injection on MongoDB-backed endpoints
# 4. GraphQL injection
23.9 MedSecure API Security Considerations
Healthcare APIs have additional security requirements driven by regulatory frameworks:
FHIR API Security: Fast Healthcare Interoperability Resources (FHIR) is the standard API for healthcare data exchange. MedSecure implements FHIR endpoints:
# Patient data access
GET /fhir/Patient/12345 HTTP/1.1
Authorization: Bearer <clinician_token>
# Test BOLA - can a clinician access patients outside their care team?
GET /fhir/Patient/99999 HTTP/1.1
Authorization: Bearer <clinician_token>
# Test scope - can a billing-scoped token access clinical data?
GET /fhir/Patient/12345/$everything HTTP/1.1
Authorization: Bearer <billing_scoped_token>
HIPAA API Requirements: - All API calls must be logged with sufficient detail for audit trails - PHI must be encrypted in transit (TLS 1.2+) and at rest - Access must follow minimum necessary principle - Break-the-glass access must be logged and reviewed - API tokens must have appropriate scope restrictions
⚖️ Regulatory Context: Under HIPAA, healthcare APIs that expose PHI without proper authorization controls constitute a breach. Under the 21st Century Cures Act, healthcare organizations must provide patient access to their data through APIs, but this access must be properly secured. The tension between accessibility and security makes healthcare API testing particularly important.
23.10 Building an API Testing Lab
Recommended Vulnerable APIs
# docker-compose-api-lab.yml
version: '3.8'
services:
# OWASP crAPI (Completely Ridiculous API)
crapi-web:
image: crapi/crapi
ports:
- "8888:8888"
# OWASP Juice Shop (includes API vulnerabilities)
juice-shop:
image: bkimminich/juice-shop
ports:
- "3000:3000"
# VAmPI (Vulnerable API)
vampi:
image: erev0s/vampi
ports:
- "5000:5000"
# Damn Vulnerable GraphQL Application
dvga:
image: dolevf/dvga
ports:
- "5013:5013"
# OWASP DevSlop Pixi
pixi:
image: devslop/pixi
ports:
- "8000:8000"
Practice Progression
- Start with VAmPI: Simple REST API with clear vulnerabilities
- Progress to crAPI: More complex, realistic API with multiple vulnerability types
- GraphQL with DVGA: Practice GraphQL-specific attacks
- Full assessment with Juice Shop: Apply complete methodology
23.11 API Security Testing Automation
Building a Reusable Testing Framework
#!/usr/bin/env python3
"""
API Security Testing Framework - ShopStack Assessment
Automates common API security checks
"""
import requests
import json
from urllib.parse import urljoin
class APISecurityTester:
def __init__(self, base_url, auth_token=None):
self.base_url = base_url
self.session = requests.Session()
if auth_token:
self.session.headers['Authorization'] = f'Bearer {auth_token}'
def test_bola(self, endpoint_pattern, id_range):
"""Test for Broken Object Level Authorization"""
results = []
for obj_id in id_range:
url = urljoin(self.base_url, endpoint_pattern.format(id=obj_id))
response = self.session.get(url)
if response.status_code == 200:
results.append({
'id': obj_id,
'status': response.status_code,
'data_size': len(response.content)
})
return results
def test_mass_assignment(self, endpoint, base_data, extra_fields):
"""Test for mass assignment vulnerability"""
results = []
for field, value in extra_fields.items():
test_data = {**base_data, field: value}
response = self.session.put(
urljoin(self.base_url, endpoint),
json=test_data
)
# Check if the field was accepted
get_response = self.session.get(
urljoin(self.base_url, endpoint)
)
if field in get_response.json():
results.append({
'field': field,
'accepted': True,
'value': get_response.json().get(field)
})
return results
def test_rate_limiting(self, endpoint, method='GET', count=100):
"""Test rate limiting on an endpoint"""
status_codes = []
for i in range(count):
if method == 'GET':
r = self.session.get(urljoin(self.base_url, endpoint))
else:
r = self.session.post(urljoin(self.base_url, endpoint))
status_codes.append(r.status_code)
if r.status_code == 429:
return {
'rate_limited': True,
'limit_hit_at': i + 1,
'retry_after': r.headers.get('Retry-After')
}
return {
'rate_limited': False,
'requests_sent': count,
'status_distribution': {
code: status_codes.count(code)
for code in set(status_codes)
}
}
This framework provides a starting point for automated API security testing. Extend it with additional test cases specific to your target application.
23.12 Summary
API security testing has become one of the most critical skills for ethical hackers as organizations increasingly expose business logic through APIs. This chapter covered the full spectrum of API security assessment, from reconnaissance and documentation discovery through the complete OWASP API Security Top 10.
The key insights for API security testing are:
- APIs expose business logic directly. Unlike web UIs that mediate interactions, APIs allow attackers to interact with backend systems in unexpected ways.
- Authorization is the primary weakness. BOLA/IDOR vulnerabilities are consistently the most common and impactful API security flaws.
- Different API paradigms require different techniques. REST, GraphQL, and gRPC each have unique attack surfaces that require specialized testing approaches.
- Business logic flaws are API-specific. Automated scanners cannot detect workflow bypasses, race conditions, or semantic manipulation — manual testing is essential.
- API inventory management is a growing challenge. Shadow APIs, deprecated versions, and undocumented endpoints represent significant risk that many organizations fail to manage.
The Optus, T-Mobile, and Peloton breaches demonstrated that even large, well-resourced organizations can fail at basic API security. As an ethical hacker, your systematic application of the techniques in this chapter helps organizations identify and remediate these vulnerabilities before attackers exploit them.
In the next chapter, we move beyond web application exploitation to examine client-side attacks, exploring how attackers target end users through cross-site scripting, cross-site request forgery, and other browser-based attack vectors.
Key Terms
| Term | Definition |
|---|---|
| BOLA | Broken Object Level Authorization; accessing objects belonging to other users |
| CORS | Cross-Origin Resource Sharing; browser security mechanism for cross-domain API access |
| FHIR | Fast Healthcare Interoperability Resources; standard API for healthcare data |
| GraphQL Introspection | Built-in schema query system revealing the entire API structure |
| gRPC | Google Remote Procedure Call; binary RPC framework using Protocol Buffers |
| IDOR | Insecure Direct Object Reference; synonym for BOLA |
| Kiterunner | API endpoint discovery tool using route signature matching |
| Mass Assignment | Binding user input to internal object properties without filtering |
| OWASP API Security Top 10 | Industry-standard classification of API security risks |
| Rate Limiting | Restricting the number of API requests within a time window |
| REST | Representational State Transfer; resource-oriented API architecture |
| Shadow API | Undocumented or unknown API endpoint in production |
| Swagger/OpenAPI | API specification format for describing REST APIs |
Next Chapter: Chapter 24: Client-Side Attacks