Request access
Articles

The access control a scanner cannot see

Broken access control is the single most exploited class of web flaw, and it is invisible to a tool with one login. The test is one account reaching for another's data.

2 min read

The most common serious flaw in web applications right now is not an exotic injection. It is a page that loads data it should have refused to load. Broken access control sits at the top of the risk lists because it is everywhere, and because the usual tools walk straight past it.

The reason they walk past it is simple. A scanner logs in once, as one user, and checks whether the pages that user is allowed to see respond. They do. Nothing looks wrong. The flaw only appears when a second user asks for the first user's data, and a tool with one account never asks that question.

One object at a time

Take an order page. Signed in as yourself, you open your own invoice.

GET /api/orders/1001    (session: alice)   ->   200  { total, address, items }
GET /api/orders/1002    (session: alice)   ->   200  { total, address, items }

The first request is fine. The second is the finding. Order 1002 belongs to another customer, and the server handed it over because it checked that Alice was logged in but never checked that the order was hers. That is an insecure direct object reference, and it reads bank statements, downloads other people's documents, and edits records that were never yours, one identifier at a time.

The same gap appears one level up. A field on a shared type that only an administrator should write. An endpoint that trusts a role sent by the client. A tenant boundary that holds for the list view and leaks on the detail view. Each is the same mistake: the code confirmed who you are and forgot to confirm what you may touch.

How we test it

We test with two accounts, on purpose. Everything one account can reach, we ask for again from the other, changing only the identifier. We walk the object references the application hands out and try each one from a position that should be refused. We send the roles and flags a client is not meant to set and see whether the server believes them.

Then every hit is reproduced by hand, because access control is exactly where an automated guess goes wrong. A 200 can be an empty template. A 403 can hide a side effect that still happened. We confirm that real data crossed a boundary before we call it a finding, and we attach the two requests that prove it.

Authentication asks who you are. Authorization asks what is yours. Most tools only test the first.

Access control cannot be scanned into safety, because the question is about your data model, not your response codes. It has to be walked, account against account, object against object. That is slow, it does not automate cleanly, and it is why we do it by hand and why it finds what the scan missed.