Lab 12: API Security Testing

Objective

Attack a live REST API from Kali Linux using the OWASP API Security Top 10. You will:

  1. BOLA/IDOR — access any user's orders and profile by changing the ID in the URL

  2. JWT alg:none — forge an admin token with no secret needed

  3. Mass Assignment — escalate your own role from user to admin by sending extra fields

  4. Excessive Data Exposure — read internal cost prices and supplier secrets from a public endpoint

  5. Broken Function Level Authorization — access an unauthenticated internal admin endpoint

  6. Rate Limit Bypass — defeat IP-based rate limiting with a spoofed X-Forwarded-For header

Every attack runs from Kali against a live Flask API — no simulation, all real HTTP responses.


Background

The OWASP API Security Top 10 (2023) was created because APIs fail in ways the classic OWASP Top 10 doesn't fully capture. REST APIs are attacked differently from web pages — there is no browser enforcing same-origin, no HTML form to inspect, and the API's own documentation often maps the attack surface.

Why APIs are the fastest-growing attack surface:

  • Mobile apps embed API tokens in binaries — extractable with a hex editor

  • API versioning (/api/v1/, /api/v2/) means old broken endpoints stay live

  • Developers return full ORM objects, leaking fields never meant to be public

  • Rate limiting on IPs is trivially bypassed with X-Forwarded-For headers

Real-world examples:

  • Venmo (2019)/transactions endpoint was public; 200M transactions scraped showing who paid who for what. BOLA.

  • Peloton (2021)/api/user/{userId} returned private data including location, age, weight for any user ID. BOLA.

  • T-Mobile (2023) — API returned all account data including SIM card details with no auth. Broken function-level authorization.

  • JWT alg:none — Exploited in Auth0, AWS Cognito, and multiple Node.js apps using the jsonwebtoken library before v9.


Architecture

Time

50 minutes

Prerequisites

  • Docker installed and running

  • Lab 10 or 11 completed (familiarity with the two-container setup)

Tools

Tool
Container
Purpose

curl

Kali

Send HTTP requests, exploit all API endpoints

python3

Kali

Craft JWT forgeries, automate BOLA enumeration

nmap

Kali

Port and service fingerprinting

gobuster

Kali

Enumerate API endpoints and routes


Lab Instructions

Step 1: Environment Setup — Launch the Vulnerable API

The victim runs a REST API with 3 users (admin, alice, bob) and multiple endpoints that are vulnerable to different OWASP API Top 10 issues.

📸 Verified Output:


Step 2: Launch the Kali Attacker Container

Set target and run initial recon:

📸 Verified Output:


Step 3: Get a Valid Token — Authenticate as alice

📸 Verified Output:

💡 JWT payloads are base64-encoded, not encrypted. Anyone can decode and read them without the secret. The secret only protects the signature — if the server doesn't verify the signature, the payload is fully attacker-controlled. This is why alg:none attacks are so powerful.


Step 4: BOLA / IDOR — Access Any User's Orders

BOLA (Broken Object Level Authorization) — the #1 OWASP API vulnerability. Alice is user_id=2, but the API lets her access orders belonging to any user_id.

📸 Verified Output:

💡 BOLA is #1 in the OWASP API Top 10 because it requires zero skill to exploit — just change a number in the URL. The server validates the token (is the user logged in?) but not the object (does this order belong to this user?). Fix: WHERE id=? AND user_id=? using the user_id from the verified JWT payload — never from the request.


Step 5: BOLA — Access Any User Profile (API Key Leak)

📸 Verified Output:


Step 6: JWT alg:none Attack — Forge an Admin Token

📸 Verified Output:

💡 The alg:none attack works because the server reads alg from the token header — which the attacker controls. When alg=none, the server skips signature verification entirely. Fix: never read the algorithm from the token. Hardcode it server-side: if header['alg'] != 'HS256': reject. Use a well-maintained JWT library that handles this for you (e.g., python-jose, authlib).


Step 7: Mass Assignment — Escalate Role from user to admin

📸 Verified Output:

💡 Mass assignment happens when the server blindly maps client-supplied JSON fields directly onto the data model. The developer wrote a generic "update user" handler that accepts any field — including role, api_key, and username. Fix: use an explicit allowlist of fields that users may update: allowed = {'email', 'password'}; safe_data = {k:v for k,v in data.items() if k in allowed}.


Step 8: Excessive Data Exposure

📸 Verified Output:


Step 9: Broken Function Level Authorization + Rate Limit Bypass

📸 Verified Output:

💡 X-Forwarded-For is set by load balancers and proxies — but any client can set it too. Rate limiting purely on this header is bypassable by anyone. Fix: rate limit on the authenticated user_id from the JWT payload (not the IP), and combine with IP-level limiting using the verified IP from the actual TCP connection (request.remote_addr), not the header.


Step 10: Cleanup


Attack Summary

Attack
OWASP API
Endpoint
Result

BOLA on orders

API1:2023

GET /api/orders/{id}

Read all users' orders and private notes

BOLA on users

API1:2023

GET /api/users/{id}

Read admin profile + API key

JWT alg:none

API2:2023

All authenticated endpoints

Forged admin token, no secret needed

Mass assignment

API3:2023

POST /api/users/{id}/update

Escalated role from user to admin

Excessive data exposure

API3:2023

GET /api/products

Cost prices + supplier secrets exposed

Broken function auth

API5:2023

GET /api/internal/users

All users + API keys, zero auth

Rate limit bypass

API4:2023

GET /api/search

Unlimited requests via spoofed IP header


Remediation

BOLA Fix — Always filter by authenticated user

JWT Fix — Hardcode the algorithm

Mass Assignment Fix — Explicit allowlist

Excessive Data Exposure Fix — Explicit field selection

Rate Limit Fix — Limit by user_id, not IP

Further Reading

Last updated