Break and fix broken access control
Continues from the last build: Last rung you mapped how it could be attacked and stood up two test accounts on your own instance, but the threat model is still just a document. A01 Broken Access Control sits at the top of the list and nobody has actually tried it.
Your threat model ranked Broken Access Control as the number one risk, but a ranked list on a wiki page has never stopped an attacker.
What you'll build
You will walk away with a captured, reproducible IDOR against your own instance, a server-side requireOwnership authorization layer applied to every account and transaction route (not a patch on the one URL you found), a fixed function-level access bug on an admin endpoint, and an automated regression test wired into CI that turns any future broken-access-control regression into a failing build instead of a leaked balance.
See how we teach, before you sign up
You don't just get code dumped on you. Every starter file and every solution is explained line-by-line, in plain English. Here's one real file from this project:
import { Router } from 'express';
import { authenticate } from '../middleware/authenticate';
import { getAccountById } from '../db/accounts';
const router = Router();
router.get('/accounts/:id', authenticate, async (req, res) => {
const account = await getAccountById(req.params.id);
if (!account) {
return res.status(404).json({ error: 'not found' });
}
res.json(account);
});
export default router;
Reading this file
router.get('/accounts/:id', authenticate, async (req, res) => {authenticate only proves who is asking, it never checks whether they are allowed to see this specific account id.const account = await getAccountById(req.params.id);The lookup is keyed only on the URL param, so any authenticated user can pass any id and get a row back.res.json(account);Whatever getAccountById returns, including another user's balance, is sent straight to the client with no ownership gate.
authenticate confirms a valid session exists but the handler never compares account.user_id to req.user.id, which is the whole vulnerability in one route.
That's 1 of 5 explained code blocks in this single project.
The build, milestone by milestone
- 1
Find the IDOR and capture it as evidence
5 guided stepsYou cannot fix a bug you have only read about in a threat model. Reproducing it against your own instance, with a saved request/response pair, turns 'A01 is our top risk' into 'here is the exact HTTP exchange that leaks a stranger's balance', which is what gets a fix prioritized and what you will use later to prove the regression test actually catches the old bug.
- 2
Understand why authentication did not stop this
4 guided stepsIf you patch the one route you found without understanding the root cause, you will fix the symptom and leave the same missing-ownership-check pattern in every other object route, which is exactly what the sibling-bug milestone will expose. Naming the class (missing object-level authorization, OWASP A01, CWE-639) is what lets you search the whole codebase for it instead of one file.
- 3
Fix it with a real server-side ownership check
5 guided stepsHiding the id, obscuring it, or checking ownership only in the one route you tested are all fake fixes, because the next route or the next developer who copies the pattern reintroduces the same hole. A shared, reusable ownership check applied at the data layer is the only version of this fix that survives someone adding a new account route next month.
- 4
Find the sibling bugs from the same root cause
5 guided stepsA single patched route proves you can fix an instance; auditing every route proves you fixed the class. Support and admin tooling in particular tends to get bolted on later with 'it's only for support staff anyway' as the entire security model, which is exactly the missing-function-level-access-control pattern (OWASP A01 also covers this) that a real audit catches before an attacker does.
- 5
Prove it with a test that fails on the old code
5 guided stepsA fix you verified manually once can silently regress the next time someone edits the routes file. A test that is proven to fail on the vulnerable version and pass on the fixed version is the only artifact that keeps this bug fixed six months from now when you are not the one reviewing the diff.
What's inside when you start
You'll walk away with
This is portfolio-grade. Build it free.
Sign up to unlock every milestone step-by-step, the code skeletons, full reference solutions, and checkable tasks, with your progress saved as you build.
Start building