Most IDOR write-ups stop at "change the ID in the URL and you get someone else's data."
That's the easy part. The part that actually matters — proving it rigorously, ruling out
the boring explanation, and turning it into a report someone can act on — usually gets
skipped.
I recorded a full walkthrough doing exactly that, end to end, against a deliberately
vulnerable local training target.
{% youtube 8RlBRBQBUAw %}
The bug
A simple invoice endpoint, GET /invoices/:id. It checks that someone is logged in
(401 if not), but never checks that the logged-in identity actually owns the invoice
it's returning:
invoices.find(inv => inv.id === Number(req.params.id))
// no invoice.owner === session.user check
Log in as any user, change the number in the URL, read anyone's invoice. Classic
CWE-639 — Authorization Bypass Through User-Controlled Key, and it maps directly to:
OWASP A01:2021 — Broken Access Control
API1:2023 — Broken Object Level Authorization (BOLA)
What the walkthrough actually covers
The video isn't just "look, it's broken" — it's the full evidence chain a real assessment
needs:
TimestampPhase
0:00Capture two separate identities (alice, bob)
1:26:19Prove cross-owner access live, in-browser
2:06:26Pull raw request/response evidence from the network tab
2:52:04Run a negative control — unauthenticated request, confirm 401
4:08:06Confirm independently with an automated IDOR probe
6:12:03Create a structured finding (CWE/OWASP/API mapping + evidence)
8:12:07Structure the evidence for review
10:29:00Export a report
The negative control is the step people skip most often, and it's the one that separates
"broken access control" from "there's just no auth at all" — an important distinction if
you're writing this up for a real triager.
Why the control matters
Without it, you've only shown that an authenticated user can read someone else's data —
you haven't ruled out the (much less interesting) possibility that the endpoint has no
auth check whatsoever. Hitting the same endpoint with zero session and getting a clean
401 proves the app does enforce authentication — it just never enforces ownership.
That's the difference between a one-line "missing auth" bug and a systemic
authorization-model gap.
Fix
Load the object, then check ownership before returning it:
const invoice = invoices.find(inv => inv.id === Number(req.params.id));
if (!invoice || invoice.owner !== session.user) {
return res.status(404).end(); // or 403 — avoid leaking existence via 401 vs 403
}
Every object access needs to be authorized, not just authenticated — that's the whole bug
in one sentence.
All testing shown is against a local, self-hosted, intentionally vulnerable training app —
offline, no unauthorized access. If you're learning web app security, bug bounty hunting,
or just want to see what "proving" a vuln looks like beyond a screenshot, the full video
is above.
Try it yourself → [https://github.com/sendwavehub/scan-target-demo-apps][1]
Windows Store [https://apps.microsoft.com/detail/9pj0j7bk1m27?hl=en-US][2]
Web Site [https://Sendwavehub.tech/en/apps/ai-security-studio-4][3]
[1]: https://github.com/sendwavehub/scan-target-demo-apps
[2]: https://apps.microsoft.com/detail/9pj0j7bk1m27?hl=en-US
[3]: https://Sendwavehub.tech/en/apps/ai-security-studio-4