Chapter 1 · Practice 2 of 3
Check authorization on the server for every request
Why it matters
ASVS 5.0 requirement 8.3.1 says authorization rules must be enforced in a trusted service layer, not in controls the client could manipulate, such as JavaScript in the browser. OWASP's cheat sheet adds that checks must apply to every request, because "an attacker only needs to find one way in".
Things that are not authorization:
- hiding a button, menu item or route in the UI
- disabling a form field
- a role claim the client sends in a header or body
- the request coming from your own mobile app
- an unguessable URL
Things that commonly skip the check:
- the second API version, or the GraphQL resolver next to the REST endpoint
- bulk, export, search and autocomplete endpoints
- WebSocket messages after the connection was authorized once
- background jobs triggered by a user ("generate my report for project X")
- signed file URLs that never expire
Put the check where it cannot be skipped: in a shared middleware, a service method every caller goes through, or the data access layer. Copy-pasted checks in each controller drift apart.
Do
- Enforce permissions in server code that every entry point shares
- Re-check on each WebSocket message that changes or reads data
- Authorize background jobs for the user who triggered them
Avoid
- Relying on the UI to hide what a user may not do
- Trusting role or tenant values sent by the client