Skip to content
A pentester found an IDOR in our statements API in 10 minutes. The fix was boring
experience2 min read141 viewsSep 22, 2026

A pentester found an IDOR in our statements API in 10 minutes. The fix was boring

Fatima Al-Sayed@fatima_appsec

September 22, 2026 · 2 min read

#1 in Security Theatre

141 views3 comments

Last quarter an external pentest found that any logged-in customer could download any other customer's account statement. It took the tester around 10 minutes. I want to share it because the bug was very ordinary, and ordinary bugs are the ones that survive reviews.

The endpoint was GET /api/statements/{id}. It checked that the request had a valid session. It did not check that the statement belonged to the customer in that session. Statement ids were sequential integers, so the tester downloaded their own statement, changed 48213 to 48212, and got a stranger's name, address and transactions.

How did it pass review? The authentication middleware was there, so it "looked secure". And the developer copied the pattern from an internal admin endpoint, where any staff user may see any statement. Same code, different authorization rule.

The fix:

select * from statements
 where id = $1
   and customer_id = $2   -- from the session, never from the request

If no row comes back, return 404, not 403. A 403 tells the attacker "this id exists, you just cannot have it".

What we changed in the process, which matters more than the fix:

  • Every endpoint that takes an object id now has an automated test that logs in as user A and requests user B's object. One helper function, two lines per endpoint.
  • Data access goes through repository functions that require the owner id as a parameter. You cannot forget it, the code does not compile without it.
  • We did not switch to UUIDs as "the fix". UUIDs make ids harder to guess, which is nice, but ids leak in URLs, logs and emails. Unguessable is not the same as authorized.

We found 3 more endpoints with the same pattern in the week after, before anyone outside did. Check yours today, it takes one afternoon.

Written by

Fatima Al-Sayed

Application security engineer at a bank in Dubai. Threat modelling, auth flows, secure code review. I will ask where your JWT secret lives. Arabic coffee enthusiast.

Get the best of #security-theatre weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

3

Sign in to join the conversation.

Sign in to comment
Siobhan O'Rourke@siobhan_tests#6 in Security Theatre
we added an "other user's id" test to every API spec after a similar one. one helper, two users, every endpoint. caught 4 more. it's the cheapest security test there is and nobody writes it
1 net votes
Yuki Tanaka@yuki_ios#6 in Security Theatre
Returning 404 instead of 403 is good point, I did not know this.
1 net votes
Tomasz Kowalski@tomasz_k#3 in Security Theatre
UUID is not authorization. Thank you for writing this, I say it in every code review and nobody listen.
1 net votes