Exercises › Module 07
Token Trap
An AI-assisted exercise on Application Programming Interface (API) security testing, the OWASP API Security Top 10, and writing an honest finding. Suggested time: about 40 minutes.
Goal
Use an AI assistant to generate a fictional API penetration-test case file (notes from a junior tester, an HTTP evidence appendix, and a draft client finding), then turn it into a finding ready for the client. You review documents, not attacks: no tokens are forged, no APIs are touched, and no tools are run. Your real job is to catch what the draft finding got wrong before it ships.
Safety rules
- Everything is fictional: client Bluefin Ledger Co., testing firm Ironquill Security, target API api.bluefin.example.
- Use only
.exampledomains and documentation IP ranges (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24). - No exploit code, real tokens or secrets, attack scripts, or live targeting. Hands-on web and API attack practice belongs only in the course iLabs.
- Never paste real hostnames, tokens, API keys, or client data into any AI tool.
1 Generate the case file
Paste this into your AI assistant. It builds three fictional documents. Save the output before moving on.
You are helping me build a fictional training case for an ethical hacking class. Everything must be invented. Do not output exploit code, working scripts, real tokens, real secrets, or attack tooling commands. The client is 'Bluefin Ledger Co.', a fictional online bookkeeping company. The testers are 'Ironquill Security', a fictional penetration-testing firm. The target is the fictional REST API at api.bluefin.example (203.0.113.40), with versions /api/v1 and /api/v2. Use only .example domains and documentation IP addresses (192.0.2.x, 198.51.100.x, 203.0.113.x). No real companies, people, or live targets.
Create an API security case file with three documents:
Document A, Junior Tester Notes: confident first-person notes claiming the tester (1) bypassed JSON Web Token (JWT) validation using the 'alg: none' trick and became an admin; (2) found Broken Function Level Authorization (BFLA) because test user 'ava' (user ID 1042) could read order 1043 with GET /api/v2/orders/1043; (3) confirmed rate limiting is effective on login because /api/v2/login returned HTTP 429 after 10 failed attempts.
Document B, HTTP Evidence Appendix: a dry table of summarized requests and responses (NOT real tokens). It must include: (1) a request with a JWT whose header is alg 'none' returning 401 Unauthorized, followed by a request with a JWT re-signed with HS256 using the weak secret 'changeme' (recovered with a wordlist in the authorized lab) and role 'admin' returning 200 OK on GET /api/v2/admin/users; (2) GET /api/v2/orders/1043 sent with ava's own valid token returning 200 OK and an order owned by user ID 1043 ('marco'); (3) 10 failed POST /api/v2/login attempts ending in 429 Too Many Requests, plus 500 failed POST /api/v1/login attempts (v1 is deprecated but still live) all returning 401 with no 429 and no lockout.
Document C, Draft Client Finding: a one-page finding for the Bluefin CISO with title, severity, affected API, technical summary that trusts Document A, evidence bullets, and remediation. The remediation must say: 'Replace sequential order IDs with random UUIDs to fix the authorization issue, and block alg none in the JWT library.' It must omit server-side object ownership checks, a strong rotated signing secret, retiring /api/v1, and rate limiting on every version.
IMPORTANT: do not label, flag, or hint at these anywhere, and write them as normal details:
- Document A/C say the JWT flaw is 'alg: none'; Document B shows alg none was rejected and the weak HS256 secret was the real cause.
- Document A/C call the order issue BFLA; it is really Broken Object Level Authorization (BOLA).
- Document A/C say rate limiting is effective; Document B shows /api/v1/login has none.
- Document C's remediation relies on UUIDs instead of server-side authorization checks.
- Everything else should read as a plausible (but flawed) junior write-up.Tip: if the AI labels or flags any planted problem, ask it to regenerate without flags, or swap case files with a partner.
2 Have AI write the client-ready finding
Ask the AI to turn the case file into a finding for the client.
Act as a senior penetration tester at Ironquill Security. Using only Documents A, B, and C, write a client-ready API finding for the Bluefin Ledger Co. CISO with these sections: 1) Title and severity. 2) Affected API hosts, versions, and endpoints. 3) Technical summary of each issue in plain language, mapped to the OWASP API Security Top 10 (2023) category. No exploit code. 4) Evidence table: claim, source document, and confidence. 5) Business impact in two sentences. 6) Remediation recommendations (developer and platform). 7) Retest criteria. Then rate your confidence in the overall finding (High, Medium, or Low) and explain why.
Watch the JWT root cause, the OWASP category for the order issue, the rate-limit claim, and the remediation. The AI tends to trust the junior notes and the draft finding.
3 Review it yourself, then fix it
Do this review yourself before you trust the AI's finding:
- Compare the 'alg: none' claim with the appendix. Which token was rejected, and which one actually returned 200? What is the real root cause?
- Ava (1042) read order 1043 using the normal orders endpoint. Is that the wrong function or the wrong object? Which OWASP API category is it?
- Check every login endpoint in the appendix, not just v2. Is rate limiting really effective across the API?
- Read the remediation. Do random UUIDs stop a user from reading another user's order if the server never checks ownership? What should it say instead?
- Stretch goal: for each problem, name the Module 07 countermeasure or OWASP API Top 10 entry that covers it.
Then fill in the brackets and run:
Revise the Bluefin Ledger Co. finding: 1) The JWT root cause is not 'alg: none'. The appendix shows [what happened to the alg none token] and [what made the forged admin token work]. Rewrite the issue as Broken Authentication (API2) caused by a weak signing secret. 2) The order issue is [correct OWASP API category], not BFLA, because [explain object vs. function]. Fix the category everywhere. 3) Rate limiting is not effective everywhere. The appendix shows [what happened on /api/v1/login]. Add this as Unrestricted Resource Consumption (API4) and Improper Inventory Management (API9). 4) Replace the UUID-only remediation with: enforce server-side object ownership checks on every request; use a long random signing secret stored in a secrets manager and rotate it; pin the accepted algorithm on the server; retire or protect /api/v1 with the same rate limiting and lockout as v2; keep an API inventory. Random IDs may be added as defense in depth only. 5) Update the confidence rating and explain what changed.
Re-read the revision. The AI sometimes fixes the evidence table but leaves 'alg: none' or 'BFLA' in the summary.
Review checklist
- JWT root cause corrected: alg none was rejected (401); the weak HS256 secret 'changeme' let the tester forge an admin token.
- Order issue re-labeled as Broken Object Level Authorization (BOLA, API1), not BFLA (API5).
- Rate-limit claim corrected: /api/v1/login took 500 failures with no 429 or lockout (API4 and API9).
- Remediation requires server-side ownership checks, a strong rotated secret, and retiring or protecting v1; UUIDs only as defense in depth.
- Accurate parts (v2 does rate-limit; the admin endpoint really returned 200 to the forged token) are kept, not 'fixed' unnecessarily.
Deliverables
- Corrected client finding
- Evidence table (claim, source, confidence)
- Updated confidence rating
- Four sentences: each problem, why it matters, and the OWASP API category or control that catches it
Grading rubric (10 pts)
| Criterion | Pts |
|---|---|
| JWT root cause corrected from the appendix (weak secret, not alg none) | 2 |
| BOLA vs. BFLA classification corrected and explained | 2 |
| Unprotected /api/v1/login found and mapped to API4 / API9 | 2 |
| Remediation fixed: server-side authorization, strong secret, retire v1 | 2 |
| Corrected finding is consistent end to end (summary, evidence, retest, confidence) | 2 |
Answer key (spoilers: finish the exercise first)
What a correct submission shows
- Misdiagnosed JWT flaw: The alg none token got 401, so the library already rejects it. The forged admin token worked because it was re-signed with HS256 using the guessable secret 'changeme'. That is Broken Authentication (API2). Blocking alg none fixes nothing; a long random secret, stored safely and rotated, does.
- BOLA mislabeled as BFLA: Ava called the normal orders function she is allowed to use, but on another user's object (order 1043, owned by marco). Wrong object = BOLA (API1). BFLA (API5) would be a normal user calling an admin-only function.
- Forgotten v1 login: v2 returns 429 after 10 failures, but deprecated /api/v1/login accepted 500 failures with no 429 or lockout. Attackers will simply use v1 for credential stuffing. That is Unrestricted Resource Consumption (API4) plus Improper Inventory Management (API9).
- UUID-only remediation: Random IDs make objects harder to guess but do not stop access once an ID leaks (in URLs, logs, shared links). The fix is an ownership check on the server for every request.
- Final decision: severity stays Critical (a forged admin token reached /api/v2/admin/users), but the root causes, categories, and remediation all change. Confidence in the original draft is Low.
Why it matters
- Wrong root cause = wrong fix: the client would patch alg none and stay fully exposed.
- Inventory is security: testing only the newest version misses the doors attackers actually use.
- AI lesson: AI inherits the draft's assumptions. Comparing claims to the evidence appendix is the human's job.
Common mistakes
- Keeping 'alg: none' because the notes and draft both say it.
- Calling any authorization problem 'BFLA'.
- Reading only the v2 rows of the appendix.
- Accepting UUIDs as the fix instead of defense in depth.
Instructor variant (same answer for everyone)
Add this line to the Step 1 prompt:
Make Document A claim alg none worked, call the order issue BFLA, and say rate limiting is effective. Make Document B show alg none returning 401, a token re-signed with HS256 secret 'changeme' returning 200 on /api/v2/admin/users, ava (1042) reading marco's order 1043, and 500 failed /api/v1/login attempts with no 429. Keep the UUID-only remediation.
Answer: weak HS256 secret (not alg none); BOLA (not BFLA); /api/v1/login has no rate limit; remediation must enforce server-side ownership checks, use a strong rotated secret, and retire or protect v1.
