Access Control Cases a Scanner Will Miss

Find access-control bugs a scanner misses: a swapped object id, a lower role, and another user's data in the response.

This post is part of the Security Testing Basics series. If you missed the previous post, read Part 2 — OWASP ZAP for QA first.


Introduction — The Scanner Had One User

ZAP crawled as Alex and reported what Alex could open. It did not log in as Blair and ask for Alex’s invoice. That case is an identifier and a second session. Scanners are bad at it. You are not.

Two Accounts, One Object

Create two users in the test environment. Give each an object the other must not see: an invoice, a message, a file. As Blair, request Alex’s object by changing the id.

# Blair's token, Alex's invoice
curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: Bearer $BLAIR" \
  https://staging.example.com/api/invoices/1001

The only passing result is 403 or 404, with a body that does not contain Alex’s amounts, address, or line items. A 200 is a defect. A 404 that still includes the line items is a defect. A 500 that includes a stack trace with the row is a defect.

Repeat for the write. Blair must not be able to pay, cancel, or comment on Alex’s invoice.

The Role, Not Only the Owner

Horizontal bugs are “another user like me.” Vertical bugs are “a role I do not have.”

Sign in as the lowest role that can open the application. Call the action you believe is reserved for an admin: export all customers, change a price, invite a teammate, read an audit log. The UI hiding the button is not the control. The request is the control. Send the request the button would have sent.

A matrix of ten roles and forty actions is a spreadsheet you will not maintain. Test the actions that expose data or move money, for the role that most users have.

The List Endpoints

A detail URL that returns 404 can still leak through the list. Open GET /api/invoices as Blair and look for Alex’s id. Filters and search count. ?userId=alex on Blair’s session is the same bug with a query string.

What You File

Steps are the two accounts, the request, and the field in the body that belonged to the other person. “IDOR” as a title is optional. “Blair can download Alex’s invoice PDF” is the bug.

Conclusion

A scanner with one session will not swap an id for you. Two users, one object, the lowest everyday role, and the list endpoint. That is the pass.

Action for this week: Pick one object a user owns. Request it with a second user’s token. If the body contains the first user’s data, file it with the response, redacted.


Next in the series: Part 4 — Sessions, Cookies, and CSRF.