In a lot of business software, user permissions are checked by the app rather than by the database — which hides the buttons somebody should not press without stopping the request behind them. If the system has an API, or ships a key to the browser, a determined person who can log in at all can sometimes reach data their role was never meant to touch. The fix is to enforce roles where the records actually live.
This is not a theoretical worry. We found exactly this in a system we had built and shipped, and this guide is partly an account of that.
Stopping a button is not stopping a request
Nearly every business system has roles. Somebody is a viewer, somebody is an editor, somebody is an admin. What varies — invisibly, from the outside — is where that gets decided.
The common arrangement is that the check lives in the application: the code that draws the page asks "is this person an editor?" and, if not, does not render the delete button. That is genuine and useful. It is also only a check on the screen. The database underneath has been told nothing, and will answer anybody who asks it politely and has a valid login.
Modern web applications make this sharper than it used to be, because many of them talk to the data over an API using a key that ships inside the browser. That key is not a secret — it is in the page source on every visit, by design. So the question is not whether someone could get it. It is whether the data would say no when they used it.
What we found in our own build
We built a portfolio system for a client, and it went live at the start of July 2026. Two weeks later, reviewing it, we found the roles were being enforced in our application code and nowhere else. Every rule on the data asked one question — is this record part of your organisation? — and stopped there.
Two consequences followed from that, and neither was acceptable:
- Someone invited purely as a viewer — a person meant to look and nothing else — could in principle have reached past the interface and deleted a whole company record.
- The rule governing a person's own profile row let them change any part of it, including the field holding their role. Anyone able to log in could in principle have made themselves an admin.
Both were fixed on 15 July 2026, in two changes that moved the role checks into the database itself. We are not going to tell you nobody exploited them, because we cannot prove a negative and would not insult you by trying. What we can tell you is that we went looking, in a system that was already live and already working, and that this is the kind of thing a review finds and a demo never does.
Why it happens, and why it is not incompetence
It is the default. The application is where you write features, so it is where the checks naturally get written. Everything looks correct while you are testing, because you test through the interface, and through the interface it is correct. The gap only shows up if you deliberately step around your own front end and ask the data directly — which is not a thing anyone does by accident.
The National Cyber Security Centre's guidance on product design puts the requirement plainly: a product "should support role-based authentication and access control", and "all requests for access should be authenticated before being granted, so that users are only given access to the data and functionality to which their role entitles them". All requests is the part that does the work. A request that goes around your interface is still a request.
What good looks like for a small firm
You do not need an enterprise security programme. Three things carry most of the weight:
- Roles enforced at the data, not just the screen. Ask where the check happens. "In the app" is a real answer, and an incomplete one.
- Separation between customers. Each firm's records in their own space, so no arrangement of clicks reaches another company's data. We give every client their own separate database rather than a shared one with a column marking who owns each row.
- Least privilege, reviewed after real use. Give people the narrowest access that lets them work, then look again once you can see what they actually do. Our own permissions were tightened after launch, not before, because that is when the evidence existed.
Questions worth asking your supplier
- Where are user roles enforced — in the application, or in the database?
- If someone took the key out of the browser and called the API directly with their own login, what would they be able to read or change?
- Is my company's data in its own space, or in a shared table alongside other customers?
- Has anyone reviewed the permissions since the system went live, and what did they find?
- What can a "viewer" actually do if they try?
That last question is the useful one, because the honest answer is rarely "nothing" and a supplier who says "nothing" instantly has probably not checked.
None of this requires a certification or a security budget. It requires somebody to go and look, after the thing is running, at a system nobody is currently complaining about. If you want us to look at yours — or to tell you what we would check — get in touch. You can also read what our own arrangement is on the Proprietary Database page.