Stop 17 of 42 · Account Access · IDOR / BOLA

Peeking at someone else’s account

A customer shows their ID at the door and is let in. At the counter they say, “show me account 1042.” The teller fetches it, without checking that account 1042 is theirs. Nothing stops them asking for 1043, 1044, 1045…

Severity Medium to CriticalCategory IDORAccount AccessSources checked 25 Oct 2026

Watch it happen

Play it as

Mallory is logged in as account 1042 and asks the teller for account 1043. The teller hands over Alice’s statement without checking whose it is.

Arrival

Mallory is a real customer. At the door she proves who she is and gets in. So far, everything is fine.

Mallory says: Hi, I’m Mallory.
1 / 5

In bank terms and app terms

In the bankIn your app
Account numberThe ID in the URL or request, like /orders/1042
Badge at the doorBeing logged in
Checking the owner listChecking the record belongs to the logged-in user
TellerYour API endpoint

Are you exposed?

You might be exposed if…
  • Your API loads a record by ID straight from the URL or request body.
  • You only test by logging in as yourself and clicking around.
  • IDs are guessable numbers (1, 2, 3…) rather than random.
What to do about it
  1. On every request, check the record belongs to the current user before returning it.
  2. Put the ownership check in the query itself: WHERE id = ? AND user_id = ?.
  3. Use random IDs as a second layer, never as the only layer.

Track these in your to-do list →

It has happened to real companies

Real-world breaches

First American Financial (2019) — A flaw on First American's website exposed about 885 million files, including bank account numbers and statements, mortgage and tax records and Social Security numbers. No authentication was required: changing one digit in a document link returned other people's records, which suggested the document numbers were sequential. The company was alerted by a journalist in May 2019, and in 2021 the SEC fined it $487,616 because senior executives had not been told that its security team had found the flaw earlier.

Parler (2021) — In January 2021, as AWS was about to cut Parler off, an archivist pulled the URLs of every public post "in sequential order, from the very first to the very last" and archived them, and Parler had not stripped metadata such as GPS coordinates from uploaded media. Sequential, guessable IDs make bulk downloads like this easy.

What it looks like in code

// BAD: No ownership check — any authenticated user can access any task
export async function GET(request, { params }) {
  const task = await db.getById("tasks", params.id)
  return NextResponse.json(task)
}

// BAD: User ID comes from request body, not from auth token
export async function GET(request) {
  const { userId } = await request.json()
  const profile = await db.getById("profiles", userId)
  return NextResponse.json(profile)
}

Ask your AI tool to fix it

Paste this into Cursor, Claude, Lovable, Bolt or whatever you build with.

Prompt
Review every API route that reads, updates or deletes a record by ID. For each one, make sure the query also filters by the authenticated user’s ID (or checks ownership) and returns 404 or 403 when it does not match. List the routes you changed.

Where this comes from

The facts, figures and fixes on this page were checked against these sources on 25 Oct 2026. They are linked so you can check for yourself.

Spot a mistake or something out of date? Report a correction · How these guides are made and checked

Go deeper in isitsecure

The free, open-source scanner checks for most of the problems in these guides.