Many user groups, one interface
The platform serves many separate user groups. Each group's records are meant to be visible only to its owner and the users that owner authorizes.
Most of the data is read and changed through the same interface, so a single missing check can expose records across users and across groups.
The screen enforces the rules. The API has to enforce them too.
A user sees only their own accounts, transactions, invoices and notifications.
Any record, as long as the request names its identifier. The server does not confirm the caller owns it.
Every role, every function, every owner
Map the application
Catalogue every function, data type and identifier the platform exposes.
Assume every role
Work from the point of view of each of the three roles, each with its own valid session.
Test every combination
Run each role against each function, and against records owned by other users and other groups.
Prove and report
Record the exact request and response for every confirmed violation, then report it with its impact.
| Function | Role A | Role B | Role C |
|---|---|---|---|
| Account details | Violation | Not tested | Violation |
| Transactions | Violation | Not tested | Violation |
| Invoices | Violation | Not tested | Not tested |
| Notifications | Violation | Not tested | Not tested |
Enforced = server refused the request. Violation = server allowed it. Not tested = not covered in this engagement.
Ownership was never checked on the server
Role boundaries not enforced
A user from one group could read and act on records belonging to a different group. Separation between groups, which should be the strongest boundary on the platform, was not enforced at the API layer.
Object-level checks missing on reads
Endpoints that return a single record accepted any valid identifier. The server returned full records belonging to other users, including personal and financial details.
Object-level checks missing on writes
Endpoints that change state did not confirm ownership before acting. Other users' records could be modified, including changes that affect their accounts and their notifications.
Predictable identifiers
Record identifiers were sequential and easy to guess. This turned each individual gap into a practical way to enumerate the whole data set, rather than a one-off exposure.
Several further leads were noted during testing but are not yet confirmed. They are not counted in the findings above.
Risk, investigation, and trust
An authorization gap of this kind carries direct risk. Records can be read or changed by someone with no link to them, and the platform cannot easily show which records were affected.
Investigation is costly. Every affected record has to be traced, and the scope cannot be bounded until logs are reviewed for every role and every identifier. Notification obligations follow from that scope, and they arrive on the same timeline as the investigation.
Trust is the largest exposure. A platform that holds financial and personal data is judged on whether it keeps other people's records private. A single confirmed case is enough to change that judgment.
The takeaway for financial platforms
Authorization checks belong on every endpoint, enforced on the server, and verified by testing every role against every function.
Find what your last pentest missed.
Role-by-function testing finds the gaps that scanners and single-role reviews do not.
Talk to Perfai →Client details have been withheld. This case study is anonymized and describes a representative engagement.