Roles and Permissions in a Web App: Getting Access Control Right
Signing in tells a web app who someone is. Permissions decide what that person can see and change once they are in. Most of the serious mistakes happen at this second step: a customer who can open another customer's invoice, or a staff account that can do more than its job needs. This guide covers the main ways to model permissions, what the security references say goes wrong, and what to agree with your developer before the build. Checked on 5 November 2026.
Why access control matters
The OWASP Authorization Cheat Sheet separates the two steps clearly: authorization is "distinct from authentication which is the process of verifying an entity's identity." Sign-in, passwords, passkeys and sessions are covered in our web app login guide. This guide starts once the user is signed in.
Getting it wrong is common. In the OWASP Top 10:2025, Broken Access Control is A01, "maintaining its position at #1 in the Top Ten", and OWASP reports that "100% of the applications tested were found to have some form of broken access control." OWASP explains that failures "typically lead to unauthorized information disclosure, modification or destruction of all data, or performing a business function outside the user's limits."
OWASP's cheat sheet singles out horizontal privilege escalation, "being able to access another user's resources", as "an especially common weakness". Vertical escalation is the other kind: a normal user reaching admin functions.
Roles, attributes and relationships
There are three common ways to model who may do what. Most real apps mix them.
- Role-based (RBAC). The NIST glossary defines it as a model "where permitted actions on resources are identified with roles rather than with individual subject identities." NIST's RBAC project page says it was formalized in 1992 by David Ferraiolo and Rick Kuhn and adopted as an ANSI/INCITS standard in 2004, revised in 2012. NIST now marks that project as archived and no longer updated. In RBAC, "each user is assigned one or more roles, and each role is assigned one or more privileges".
- Attribute-based (ABAC). Decisions use attributes of the user, the record and the situation, such as department, record owner, time of day or device. OWASP's example is a sales representative who may open the customer database "from the internal network during working hours, but not from home at midnight."
- Relationship-based (ReBAC). Access follows the link between a user and a record. OWASP's example is "allowing only the user who created a post to edit it."
OWASP's advice is that "ABAC and ReBAC should typically be preferred for application development". It says pure RBAC "does a poor job of supporting object-level or horizontal access control decisions" and is "poorly suited" when several organizations or customers share the same system. That matters for any app that serves many client companies, which our multi-tenant guide covers. In practice, roles are still a good way to describe job functions to people. Record-level rules, such as seeing only your own company's invoices, are what ABAC and ReBAC handle well, and you will need both.
Designing roles that last
Start with least privilege, which OWASP describes as "assigning users only the minimum privileges necessary to complete their job". It applies sideways as well as up and down: OWASP's example is that an accountant "should likely not be granted access to a customer database" and a sales representative should not see payroll data, even at the same level of the organization.
Watch the number of roles. OWASP warns that "role explosion" can occur "when a system defines too many roles", and that as roles multiply, "both testing and auditing ... become more difficult." A few clear roles combined with record-level rules usually age better than a new role for every exception.
Plan for change as well. OWASP recommends reviewing permissions after launch for "privilege creep", where users end up with more access than the design intended. It also notes that "it is easier to grant users additional permissions rather than to take away some they previously enjoyed", so start narrow. Our customer portal guide shows how to lay roles and actions out in a simple table and plan how accounts are created and removed.
Checking each record, not just each page
A common hole is a page that checks the user's role but not the record. OWASP's IDOR cheat sheet describes a URL like /users/123: if changing it to 124 shows another user's details, the app is vulnerable. The identifier does not have to be a number in the address. OWASP lists hidden form fields, file names, account numbers and tokens.
Long random IDs help only a little. OWASP says that "even with complex identifiers, access control checks are essential." The Authorization Cheat Sheet puts the rule simply: "Just because a user has access to an object of a particular type does not mean they should have access to every object of that particular type." The Top 10 entry asks for access rules that "enforce record ownership rather than allowing users to create, read, update, or delete any record."
Where the checks must run
OWASP is firm that developers "must never rely on client-side access control checks." Hiding a menu item or greying out a button is fine for usability, but the server, API gateway or serverless function has to refuse the request as well. The Top 10 entry lists "an accessible API with missing access controls for POST, PUT, and DELETE" among common failures, which is why mobile apps and single-page front ends that talk to an API need the same checks.
OWASP also recommends checking permission "on every request" with application-wide middleware or filters rather than code repeated in each function, because "an attacker only needs to find one way in". Deny by default, and configure it explicitly instead of trusting a framework's defaults, which OWASP notes "may evolve over time".
Files and cloud storage
Uploaded documents, exports and images need the same rules as database records. OWASP notes that "poorly secured resources in cloud storage offerings (such as Amazon S3 Buckets) have risen to prominence", and recommends protecting static files "using the same access control logic and mechanisms" as the rest of the app. A file link that works for anyone who has it is a permission decision too. Our file uploads guide covers storing and serving those files.
Failures, logs and tests
OWASP says "failed access control checks are a normal occurrence in a secured application". They should end in a plain refusal without leaking detail in error messages. The Top 10 entry recommends that you "log access control failures, alert admins when appropriate (e.g., repeated failures)." Our guide to audit logs in web apps covers which events to record and what each entry needs.
Tests matter more here than almost anywhere. OWASP's cheat sheet asks for tests "that validate that the permissions mapped out in the design phase are being correctly enforced", and the Top 10 entry says developers and QA staff "should include functional access control in their unit and integration tests." A useful habit is to keep two test accounts from different customers and check that neither can reach the other's records through the interface or the API.
What to put in the brief
- Every type of user, including your own staff and any partner or contractor accounts.
- A table of roles against actions, plus the record-level rule for each, such as own company only.
- Who can invite users, change roles and remove access, and what happens when someone leaves.
- Whether any staff role can see customer data, and on what basis.
- How files and exports are protected.
- Logging of refused requests, and automated tests for the permission table, including cross-customer tests.
Frequently asked questions
What is the difference between authentication and authorization?
Authentication checks who someone is, for example with a password or passkey. Authorization decides what that signed-in person may see and do. OWASP treats them as separate steps, and a signed-in user is often not allowed to do everything the system can do.
Is role-based access control enough?
For simple apps, roles may cover most needs. OWASP's Authorization Cheat Sheet says attribute-based and relationship-based models should typically be preferred for application development, because roles alone handle record-level and multi-customer rules poorly. Most apps combine roles with record-level rules.
Do random IDs stop users seeing each other's data?
No. OWASP says that even with complex identifiers, access control checks are essential. The server has to check that the signed-in user may access that specific record on every request.
Is hiding a button enough to stop a user doing something?
No. OWASP says developers must never rely on client-side access control checks. The server or API must refuse the request as well.
How should permissions be tested?
OWASP recommends unit and integration tests that confirm the permissions from the design are enforced. Include tests where one customer's account tries to reach another customer's records through both the interface and the API.
Planning a web app with several kinds of users? See our web app development service and all our services, or email [email protected].