BSCPServer-side
Access control and IDOR
Access control fails when the server uses an identifier the client sent and does not check it against the caller.
18 min read · Academy topic: Access control
Objectives
- Separate authentication from authorization
- Describe horizontal and vertical access gaps
- Test with two lab identities and one change at a time
On this page
Core idea
Authentication answers “who is this session?” Authorization answers “what may this session do?” IDOR, insecure direct object reference, is the common case where an identifier in the request picks a record, and the server fetches that record without asking whether this caller may see it.
Horizontal access is between peers: one customer and another customer’s invoice. Vertical access is between ranks: a user and an administrator function. Both are missing checks. Hiding a button is not a check. The browser is not the place the decision is allowed to live.
What to look for
From the map, list every value that selects an object or an action:
- Identifiers in the path, the query, or the body
- A role name or a flag the client sends
- An administrative path you only noticed in a script or a comment
- A method change, such as a read request that has a sibling update
- A multi-step flow where step one checks the user and step two trusts a previously issued identifier
The evidence you want is a difference in whose data came back, or whether a privileged action succeeded, while your session stayed the same. If both the session and the identifier change, you have learned nothing.
In the lab / in Burp
Use two lab accounts when the lab provides them. With account A, perform the action normally and keep that request in Repeater. Note the identifier and a non-secret marker of the record (a label the lab shows you).
Then swap only the identifier for one that belongs to account B, still using A’s session. Compare status, length, and whether B’s marker appears. As a second experiment, keep A’s identifier and remove or swap the session, so you know the data is not simply public.
For a vertical question, repeat a high-privilege request from history with a low-privilege session, changing nothing else. A hidden link that still answers is a missing server-side check.
Record the comparison. Do not build a list of real identifiers from anywhere except the lab.
Defensive controls
Every object access checks the caller’s identity against an allow rule on the server. Identifiers can stay visible. Unpredictable identifiers make guessing harder and do not replace the check. Administrative actions need their own rule, not a hidden URL. Frameworks often have a place to declare this per route. The bug is usually a route that skipped it.
Common pitfalls
Testing while logged out and calling a public page an access-control pass. Changing the identifier and the cookie in one edit. Concluding the app is safe because identifiers are long. Trusting the UI: if the button is missing, the request may still exist in history from another role. Forgetting that a list endpoint and a detail endpoint can enforce different rules.
From the learning path
The December 2025 learning-path notes put the three checks in order, which this page had split across topics.
Authentication says who the session is. Session management says which later requests belong to that person. Access control says whether this request’s action is allowed. A successful login does not answer the third question.
Vertical gaps often look like a route the interface only links for an administrator, with no check on the route itself. The path can be hard to guess and still fail, because the script that draws the admin link is shipped to every browser. A hard-to-guess path is obscurity. A robots.txt Disallow entry is a hint that a path exists, not a lock on it.
Some applications store the role where the browser will send it back: a hidden field, a cookie, or a query flag. The decision then belongs to the client.
A horizontal gap becomes vertical when the other account outranks the caller. Reading a peer’s record is horizontal. Landing on an administrator through the same kind of identifier is vertical. A long random identifier is harder to guess than a counter. It is still not a check when a page the caller can already open displays that value.