A Hidden Button Is Not Security
One of the most common security mistakes in web software looks like this: an administrator should be able to delete records and an editor should not, so the delete button is hidden from editors. The screen looks right. The editor never sees the button. Everyone feels safe.
But the button was only ever a shortcut to a request the server accepts. If an editor sends that request another way, by replaying it from the browser's tools, guessing an address or calling the endpoint directly, the server happily deletes the record. Hiding the button changed what people see. It did not change what people can do.
Authorization lives on the server
In our platform, permission checks happen on the server for every operation that matters. The interface hides what a person cannot use, because that makes for a cleaner experience, but the real decision is made where the action runs.
We check permissions at every layer a request can enter:
- Pages. Opening an admin page checks whether the person may view it.
- Actions. Saving, deleting, publishing or exporting checks again at the moment of the action.
- Interactive components. Modern admin screens update parts of the page without reloading. Each of those updates is a request too, and it is checked just like a page.
- Downloads. Exports and private files are served only after confirming who is asking.
- Background jobs. Work queued on someone's behalf rechecks that they are still allowed when it runs.
- API endpoints. Every endpoint enforces the same rules as the screens.
The interactive component trap
Modern admin interfaces send small state updates back and forth between the browser and the server. Those updates can include hidden fields, such as which record is being edited or which account it belongs to. A careless component trusts those values. A careful one treats them as untrusted input.
We design components so that the server re-derives important facts, such as the current account and the record's owner, instead of accepting them from the browser. A person cannot change which account they are editing by tampering with a hidden value.
Features on hold stay closed everywhere
Sometimes we hold a module back while it is unfinished. Hiding it from the menu is not enough. When a module is on hold, its pages refuse direct visits, its interactive components refuse to load and its dashboard widgets do not appear, even for staff who are helping a customer. A module on hold is closed at every door, not just the front one.
Roles that match real jobs
Permissions are easier to enforce when they are easy to understand. Our accounts use a small set of roles with clear meanings, such as owner, administrator and member, plus finer permissions for specific modules where they are needed. Members, for example, cannot open an account's admin area at all. Clear roles make it easier to reason about what each person should be able to do, and easier to spot when the code disagrees.
Testing what the server allows
When we review a feature, we do not stop at "the button is hidden for editors." We ask what happens if an editor sends the request anyway. During browser checks we sign in with limited accounts and try direct addresses, and in automated tests we call actions as people who should be refused and confirm they are.
Errors should not leak either
A refusal should say no without revealing too much. Telling someone "you cannot delete invoice 482 for another account" confirms that invoice exists. Our refusals are plain and generic, with details kept in our logs.
The principle in one line
Every request is a question to the server: may this person do this, to this record, in this account, right now? If the server cannot answer yes with confidence, the answer is no. That principle connects to failing closed and to the way we keep each customer's data apart.
Frequently asked questions
Is hiding a button enough to restrict access?
No. Hidden buttons only change what people see. The server must check permissions for every request, because requests can be sent without the button.
What is server-side authorization?
It means the server decides whether a person may perform an action each time the action is requested, regardless of what the interface displays.
Do background jobs check permissions?
Yes. Jobs that act for a person recheck that the person and the account are still allowed when the job runs.
Comments (0)
No comments yet. Be the first to share your thoughts!