Skip to content

access

Who you are is not what you may

The two checks that look like one, and the one that is usually missing.

Almost every handler in almost every application starts the same way: find the session, and bail out if there isn't one. That check is real and it is necessary and it answers exactly one question — who is this?

The question it does not answer is whether this person may have the thing they just asked for. Those are two checks, they live in two places, and the second one is the one that is missing.

  • GET /orders/1003

    Order 1003 belongs to somebody else. Both builds know who you are; only one asks whose order it is.

The horizontal version is above: a record with an id, and an id is a thing anybody can count. The vertical version is a role — an endpoint that only administrators should reach, guarded by a session check that every customer also passes.

Neither has a signature. There is no dangerous function call and no suspicious string; the defect is a line that is not there, in a handler that otherwise reads perfectly. That is why no scanner finds these and why they are worth learning to see.

  • GET /admin/users

    The same session, signed in as a customer, against both builds.

The engine can be exact about this because the store knows whose each row is — the schema names the owner column — so 'a record belonging to somebody else reached the response' is a fact it recorded rather than a pattern in the output.

It is deliberately narrow, too: a row dragged out by an injected clause does not count. That is a different defect with a different fix, and grading it as both would be two answers to one question.

This build

shop.example.com

Nothing sent yet. Every request goes to the application in this tab and nowhere else.