The #1 web risk: apps that check who you are but not what you’re allowed to do. See IDOR and missing authorization, and enforce access control on the server every time.
Why: broken access control is the most common serious web flaw — a classic form is IDOR, where an app uses an ID from the request to fetch a record but never checks the record belongs to the current user, so changing the ID exposes someone else’s data. When: any endpoint that takes an object ID must verify ownership. Where: authentication (who you are) is not authorization (what you may access) — you need both.
# VULNERABLE — fetches the order by ID with no ownership check:
@app.get("/api/orders/{order_id}")
def get_order(order_id, current_user):
return db.orders.find(id=order_id) # any user can read ANY order
# Attacker just increments the ID: /api/orders/1001 -> /api/orders/1002
# and reads other customers' orders. This is IDOR.Why: access control must be checked on the server for every request, tied to the authenticated user — never trust a hidden field, a role in a cookie, or the UI hiding a button. When: check "is this user allowed to do this to this object?" on every sensitive action. Where: deny by default — require an explicit grant, so a forgotten check fails closed, not open.
# SAFE — scope the query to the current user (ownership enforced in the DB):
@app.get("/api/orders/{order_id}")
def get_order(order_id, current_user):
order = db.orders.find(id=order_id, user_id=current_user.id)
if order is None:
abort(404) # not theirs -> looks like it doesn't exist
return order
# Deny by default. Check authorization server-side on EVERY request.
# Hiding the button in the UI is not access control.Why: access control is tested by acting as a low-privilege user and trying to reach other users’ objects and admin functions — automated scanners miss it, so it needs manual, logic-aware testing. When: test with two accounts, swapping IDs and hitting privileged endpoints. Where: detect exploitation by alerting on users accessing objects or admin routes they should not, and on ID-enumeration patterns.
TESTING (in your lab / authorized scope):
- log in as a normal user, then try another user's object IDs
- call admin-only endpoints directly, without the admin UI
- remove/curl the request without the client-side checks
DETECTING:
- alert on a user accessing IDs far outside their normal range
- alert on non-admins hitting admin routes
- many 403/404s from one client = access-control probing