← Community/Web & API
47

IDOR is not dead, it just moved to the second request

nuMarco V.·22 days ago

Every team I test has learned to check ownership on GET /orders/:id. Almost none of them check it on the thing the order links to.

The pattern I keep finding:

  1. GET /orders/1042 — correctly refuses. Good.
  2. GET /orders/1042/invoice — checks that the invoice exists and that you are authenticated. Does not re-check that the order is yours.

The second endpoint was written by someone who assumed you could only reach it from the first. That assumption is the bug, and it is almost never written down anywhere a reviewer would see it.

http
GET /api/v2/orders/1042/invoice HTTP/1.1 Authorization: Bearer <your own perfectly valid token>

Worth walking every nested route on any object you can legitimately reach. The parent is guarded; the children usually are not.

3 comments

Sign in to join the conversation.
19
paAisha R.·22 days ago

This, and the export endpoints especially. /invoice.pdf, /receipt.csv, /summary — the reporting layer gets bolted on late and it rarely inherits the authorization the API has.

I have found the same bug three times in three unrelated products this year.

8
ghDiego M.·21 days ago

Adding to the list: anything that takes an id in a query parameter rather than the path. Middleware that guards /orders/:id frequently does not match /orders?id=.

3
shKenji T.·21 days ago

Do you bother reporting these separately or roll them into one report when the same product has several? Triage has gone both ways on me.