Manually Proving and Documenting an IDOR (CWE-639) — A Full Walkthrough

dev.to

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.

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:


js
`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:

Timestamp   Phase
0:00    Capture two separate identities (alice, bob)
1:26:19 Prove cross-owner access live, in-browser
2:06:26 Pull raw request/response evidence from the network tab
2:52:04 Run a negative control — unauthenticated request, confirm 401
4:08:06 Confirm independently with an automated IDOR probe
6:12:03 Create a structured finding (CWE/OWASP/API mapping + evidence)
8:12:07 Structure the evidence for review
10:29:00    Export 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.

Try it yourself → https://github.com/sendwavehub/scan-target-demo-apps
Windows Store https://apps.microsoft.com/detail/9pj0j7bk1m27?hl=en-US
Web Site https://Sendwavehub.tech/en/apps/ai-security-studio-4 
Enter fullscreen mode Exit fullscreen mode

Source: dev.to

arrow_back Back to News