Web Application Security and Secure Coding

Chapter 6
Authorization and Access-Control Engineering

Post Graduate Diploma in Full Stack Development
Web Application Security and Secure Coding   •   Post Graduate

6. Authorization and Access-Control Engineering

Introduction and Context

Authentication establishes who a user or service is; authorization determines what that identity is permitted to do. In a web application, this distinction is fundamental. A correctly authenticated user may still be allowed to view only selected records, perform only certain operations, or act only within a particular tenant. Authorization therefore represents the decision-making layer that protects business capabilities and data boundaries.

Access-control failures are among the most damaging weaknesses in modern applications. A missing server-side check may allow an ordinary user to access an administrator function. A predictable identifier may expose another customer’s invoice. A poorly designed role model may grant excessive privileges that remain active long after they are needed. These failures are commonly described as broken access control, privilege escalation, or insecure direct object reference (IDOR), although the broader engineering problem is inadequate authorization design and enforcement.

Authorization must be treated as an engineered security control rather than a collection of interface decisions. Hiding a button in a web page does not prevent a user from calling the underlying endpoint. A secure system makes an authorization decision on the server, using trustworthy attributes, a clearly defined policy, and the complete context of the request. It also records and tests those decisions.

Learning Objectives

After completing this chapter, the learner should be able to:

1. Distinguish authentication, authorization, access control, and accountability.

2. Compare RBAC, ABAC, policy-based authorization, and relationship-based approaches.

3. Design authorization for multi-tenant web applications.

4. Prevent IDOR, horizontal privilege escalation, and vertical privilege escalation.

5. Implement server-side enforcement at routes, services, and data-access boundaries.

6. Construct authorization policies that are testable, auditable, and maintainable.

7. Evaluate authorization risks, governance requirements, and emerging implementation trends.

Authorization as a Security Decision

An authorization decision can be represented as a function:

Decision = f(subject, action, resource, context, policy)

The subject is the requesting identity, such as a user, administrator, or service account. The action describes the operation, such as read, update, approve, export, or delete. The resource is the object being accessed. Context may include tenant, time, device assurance, network location, transaction state, or risk level. The policy defines the conditions under which the action is allowed.

For example, a policy might state: “A support agent may view a ticket if the ticket belongs to the agent’s assigned organization; an agent may update it only while it is open.” This is more precise than saying that the user has the role “support agent.” The role is one input, but ownership, tenant membership, and resource state are also relevant.

Authorization differs from related concepts:

  • Authentication verifies identity.
  • Authorization evaluates permitted actions.
  • Access control is the wider mechanism of granting, restricting, and reviewing access.
  • Accountability links actions to identities through logging and audit evidence.
  • Privilege is an authority granted to an identity or service.
  • Entitlement is a specific permission, often represented as an action-resource pair.

A secure authorization design follows the principle of least privilege: each identity receives only the access necessary for an approved purpose and period. Least privilege is not achieved merely by creating many roles. It requires careful permission definitions, separation of duties, lifecycle management, and regular review.

Models for Representing Access

Role-Based Access Control

Role-based access control (RBAC) assigns permissions to roles and assigns users to roles. A permission may be expressed as `invoice:read` or `project:approve`. A role such as `BillingClerk` receives a set of permissions, and users inherit them through role membership.

RBAC is attractive because it is understandable and administratively manageable. It works well when organizational responsibilities are stable and permissions correlate strongly with job functions. Role hierarchies can express inheritance; for example, a senior support role may include the permissions of a junior support role.

However, RBAC can become difficult to manage when policies depend on resource ownership, geography, tenant, transaction value, or time. Creating roles such as `Manager-Europe-ProjectA-AfterHours` produces role explosion. RBAC should therefore be viewed as a useful foundation rather than a complete solution for every authorization problem.

Attribute-Based Access Control

Attribute-based access control (ABAC) evaluates attributes of the subject, resource, action, and environment. Examples include:

  • Subject: department, clearance level, employment status
  • Resource: classification, owner, tenant, workflow state
  • Action: read, approve, transfer, export
  • Environment: time, device posture, network, risk score

An ABAC rule might allow a user to approve a purchase when the user’s department matches the purchase department, the user’s approval limit exceeds the amount, and the purchase is not submitted by that same user. ABAC supports fine-grained decisions, but it depends on reliable and current attributes. Incorrect or stale attributes can produce either unauthorized access or unnecessary denial.

Policy-Based Authorization

Policy-based authorization separates policy from application code. A policy engine evaluates a request and returns a decision such as permit or deny, sometimes with obligations such as masking fields or generating an audit event. The policy may be centralized in a dedicated service or expressed in a policy language and evaluated as a library.

Centralization promotes consistency across APIs and services, but it introduces operational dependencies, latency, policy deployment concerns, and the risk that developers misunderstand the policy interface. A practical architecture often combines local enforcement with centrally governed policies. The application remains responsible for identifying the resource and enforcing the result, while policy rules are managed consistently.

Relationship-Based Authorization

Relationship-based authorization evaluates how a subject is related to a resource. A user may edit a document because they are its owner, a member of the document’s workspace, or a member of a group that has access to the workspace. This model is particularly useful for collaboration platforms, repositories, project systems, and social applications.

Relationship-based systems can represent delegated access and nested groups more naturally than simple roles. They also require careful handling of graph changes, revocation, caching, and authorization consistency.

Designing the Authorization Boundary

Authorization should be enforced at the point where a business operation is executed. The user interface may improve usability by hiding unavailable actions, but it is not a security boundary. API controllers, application services, database queries, and background workers must not assume that earlier layers performed adequate checks.

A useful layered design is:

1. Route or controller check: verifies that the caller has a general entitlement to invoke the operation.

2. Service-layer check: evaluates business rules, resource ownership, workflow state, and tenant membership.

3. Data-access constraint: ensures that queries are scoped to the authorized tenant or resource set.

4. Audit layer: records the subject, action, resource, decision, and relevant reason without exposing sensitive data.

The service layer should be the principal business authorization boundary. If checks exist only in controllers, another entry point—such as a message consumer, administrative script, or GraphQL resolver—may bypass them. If checks exist only in the database, application behavior may become difficult to understand and test. Defense in depth is valuable, but each layer must have a clear responsibility.

A secure request sequence is therefore:

1. Authenticate the caller.

2. Validate the requested operation and input.

3. Resolve the target resource using server-controlled data.

4. Determine tenant and ownership context from trusted records.

5. Evaluate policy and business constraints.

6. Execute the operation only after a permit decision.

7. Record an appropriate audit event.

The system should use deny-by-default behavior. An unrecognized action, missing attribute, failed policy lookup, or ambiguous resource relationship should not silently become an allow decision.

Preventing IDOR and Privilege Escalation

An IDOR occurs when an application uses a client-supplied identifier to retrieve or modify an object without verifying that the caller is authorized to access it. The identifier may be numeric, textual, or embedded in a path. Random identifiers can reduce guessing, but they do not replace authorization.

Unsafe logic often resembles:

```text

invoice = Invoice.find(request.path.id)

return invoice

```

The secure form binds retrieval to the authorization scope:

```text

invoice = Invoice.find_by(id=request.path.id,

tenant_id=current_user.tenant_id)

require can_read(current_user, invoice)

return invoice

```

The exact implementation varies by framework, but two principles are constant: the server determines the security context, and the resource is checked before disclosure or modification.

Horizontal privilege escalation occurs when one user accesses another user’s resources at the same privilege level. Tenant-crossing invoice access is a common example. Vertical privilege escalation occurs when a lower-privileged user performs an action reserved for a higher-privileged user, such as approving payments or changing security settings.

Mass assignment creates a related risk. If an endpoint binds all client fields directly to a model, a user may submit fields such as `role`, `tenant_id`, `is_admin`, or `approved`. Use explicit allow-lists, server-controlled state transitions, and separate input models for different operations.

Tenant Isolation in Multi-Tenant Systems

A tenant is an organizational or customer boundary within a shared application. Tenant isolation means that users, services, data, configuration, files, and events are not unintentionally exposed across tenant boundaries.

Tenant identity must be derived from a trusted session, token claim validated by the server, or a membership record. It should not be accepted solely from a hidden form field, query parameter, or client-controlled header. Every tenant-scoped query should apply the tenant constraint, and every object relationship should be checked for tenant consistency.

Common architectural strategies include:

  • Shared tables with a tenant column: economical and flexible, but dependent on consistent query scoping.
  • Separate schemas: stronger structural separation, with additional migration and operational complexity.
  • Separate databases or deployments: stronger isolation and useful for regulatory or high-value workloads, but more expensive to operate.

Shared-table systems should consider database row-level security, repository methods that require tenant context, automated query checks, and integration tests that attempt cross-tenant access. Background jobs require special attention: a job must carry an explicit tenant context and must not use a global administrator connection unnecessarily.

Tenant isolation includes more than database rows. Object storage paths, cache keys, search indexes, logs, analytics datasets, email templates, and webhooks can all become cross-tenant leakage channels. Cache keys should include tenant identity; exported files should be authorized at generation and download; and logs should avoid mixing sensitive tenant data in broadly accessible systems.

Policy Engineering and Governance

Good policies are specific, composable, and explainable. They identify the protected resource, permitted action, decision subject, and conditions. Policies should distinguish ordinary read access from sensitive operations such as export, deletion, credential rotation, or approval.

Separation of duties is essential where one person should not control an entire high-risk process. For example, the creator of a payment request may be prohibited from approving it. This is a business rule, not merely a role assignment.

Authorization governance includes:

  • ownership of policy definitions;
  • approval and change-management procedures;
  • periodic access reviews;
  • joiner, mover, and leaver processes;
  • emergency access and break-glass controls;
  • evidence of decisions and administrative changes;
  • revocation after role or employment changes.

Break-glass access should be exceptional, time-limited, strongly authenticated, and audited. Administrative convenience should not create an unmonitored universal account.

Authorization logs should answer who attempted what, against which resource, when, from which service or device context, and whether the request was permitted. Logs must be protected from tampering and should not store secrets or unnecessary personal information. A denied decision can be as important to investigate as an allowed one, particularly when repeated attempts suggest abuse.

Testing Authorization as a Matrix

Authorization testing should be systematic rather than limited to a few endpoint checks. A permission matrix maps subjects or roles to actions and resources. Testing should include positive cases, negative cases, boundary cases, and transitions.

Important test dimensions include:

  • authenticated versus unauthenticated caller;
  • ordinary user versus administrator;
  • resource owner versus non-owner;
  • same tenant versus different tenant;
  • active versus suspended account;
  • permitted versus forbidden workflow state;
  • normal versus expired or revoked entitlement;
  • direct API request versus alternate endpoint or background path.

Property-based tests can express invariants such as: “No user can read a resource whose tenant differs from the user’s active tenant.” Integration tests should use multiple tenants and multiple identities. Security testing should attempt identifier substitution, method changes, parameter pollution, bulk operations, GraphQL field access, file-download manipulation, and replay of privileged requests.

A useful test result is not only HTTP status. The system should also be checked for response-body leakage, timing differences where relevant, side effects, audit records, and whether a failed authorization attempt altered state. A successful denial that still sends sensitive metadata is not a secure denial.

Applied Case: Cross-Tenant Document Exposure

Background

Consider an illustrative SaaS platform used by professional firms to store client documents. The platform uses a shared database, a React front end, and a REST API. Each document has a numeric identifier and a `tenant_id` column. Users can upload, view, share, and delete documents.

Situation and Problem

A document endpoint receives `/api/documents/1842`. The controller loads the document by ID and checks whether the caller has the `document:read` permission. All authenticated employees have that permission. The endpoint does not compare the document’s tenant with the caller’s tenant. A user who changes the identifier in the URL can therefore read documents belonging to another firm.

The front end appears correct because it displays only documents returned by the user’s normal workspace query. However, the API is the actual security boundary.

Analysis

The failure combines an IDOR with horizontal privilege escalation and inadequate tenant isolation. The role permission answers only whether the user may generally read documents; it does not answer whether this particular document belongs to an authorized tenant or workspace. The database query is also not tenant-scoped.

Further review finds that download URLs are cached by document ID alone and that a background indexing worker processes documents without recording tenant context. The vulnerability is therefore broader than one controller.

Approach and Solution

The organization introduces a resource authorization service with operations such as `can_read_document(user, document)` and `can_share_document(user, document)`. Document retrieval requires both the identifier and the authenticated tenant context. Repository methods reject calls that omit tenant scope. Cache keys include tenant ID and document ID. Download tokens contain a short expiry and are validated against the document and tenant at download time.

The service also defines separate permissions for reading metadata, reading content, sharing, deleting, and exporting. The indexing worker receives an explicit tenant identifier and uses a service identity restricted to the relevant tenant. Cross-tenant integration tests are added to the continuous integration pipeline.

Outcome

Identifier substitution no longer exposes documents. Attempts to access another tenant’s document produce a generic not-found response, avoiding unnecessary disclosure about resource existence. Administrative access is separately governed and audited. The organization also discovers and corrects similar scoping errors in search and export functions.

Learning Points

The case demonstrates that authorization is resource-specific, not merely role-specific. Tenant constraints must be applied consistently across APIs, storage, caches, workers, and exports. Random identifiers would have reduced discoverability but would not have solved the underlying authorization defect.

Common Failure Patterns and Limitations

Several practices repeatedly produce authorization weaknesses:

  • relying on hidden buttons or disabled controls;
  • trusting client-supplied roles, tenant IDs, or ownership fields;
  • checking authorization only during login;
  • using a single broad administrator permission;
  • duplicating inconsistent checks across controllers;
  • returning different detailed errors that reveal resource existence;
  • forgetting bulk endpoints, exports, webhooks, and asynchronous jobs;
  • failing to revoke access after organizational changes;
  • caching decisions longer than the underlying entitlement remains valid;
  • treating an opaque identifier as an authorization mechanism.

Central policy engines also have limitations. They may be unavailable, misconfigured, difficult to debug, or disconnected from rapidly changing business state. Policies can become so complex that no operator understands why access was granted. Every policy architecture therefore needs version control, review, test fixtures, observability, and a defined failure mode. Availability-sensitive systems may require carefully designed cached decisions, but caching must not undermine revocation or tenant boundaries.

Ethical, Regulatory, and Professional Considerations

Authorization decisions affect confidentiality, autonomy, and organizational power. Excessive access can expose health, financial, employment, or identity information. Professional engineers should minimize access, avoid discriminatory or opaque rules, and provide defensible explanations for significant decisions.

Regulatory obligations vary by jurisdiction and sector, but common expectations include least privilege, access review, segregation of duties, auditability, timely revocation, and protection of sensitive records. Compliance is not a substitute for engineering: a documented policy that is not enforced by the server provides little protection.

Privacy principles also affect authorization design. Data should not be disclosed merely because a user can access a related record. Field-level authorization may be necessary when different users can view different attributes of the same object. Authorization logs themselves should be governed because they may reveal relationships, investigations, or sensitive business activity.

Technology and Emerging Developments

Modern systems increasingly use policy-as-code, authorization-as-a-service, and relationship graphs. These approaches support consistent decisions across microservices and reduce the need to embed complex rules in every application. However, distributed authorization requires attention to policy versioning, network failure, stale relationship data, and traceability.

Continuous authorization evaluates access throughout a session or transaction rather than treating login as permanent approval. Device assurance, risk signals, transaction value, and recent authentication may influence high-impact decisions. Machine learning may help identify anomalous access patterns, but it should generally support monitoring and investigation rather than silently replacing explicit authorization rules for critical operations.

GraphQL, event-driven systems, serverless functions, and APIs expand the number of authorization enforcement points. Organizations should define common authorization libraries, service identities, policy contracts, and automated tests. The future direction is not simply more centralized policy; it is more observable, context-aware, and consistently enforced authorization.

Best Practices for Secure Access-Control Engineering

1. Define permissions as specific action-resource capabilities.

2. Enforce decisions on the server and at the business-service boundary.

3. Deny by default and fail closed when authorization data is missing.

4. Derive tenant and ownership context from trusted server-side records.

5. Scope database, cache, search, storage, and background operations.

6. Separate ordinary use from sensitive actions such as export and deletion.

7. Use allow-listed input fields and server-controlled state transitions.

8. Test horizontal and vertical escalation with multiple identities and tenants.

9. Log authorization outcomes without exposing unnecessary sensitive data.

10. Review roles, attributes, relationships, and emergency access regularly.

11. Version, review, and monitor policy changes.

12. Treat opaque identifiers, interface restrictions, and client-side checks as supplementary—not primary—controls.

Chapter Summary

Authorization determines whether an authenticated subject may perform a particular action on a particular resource in a particular context. Effective access control combines identity, permissions, resource relationships, tenant boundaries, business state, and environmental conditions. RBAC provides manageable job-function permissions; ABAC and relationship-based models support more contextual decisions; policy-based authorization promotes consistency and governance.

Secure enforcement occurs on the server, especially within business services and data-access boundaries. IDOR and privilege escalation are prevented by verifying resource ownership, tenant membership, action-specific permissions, and workflow constraints. Multi-tenant security must cover databases, caches, files, search, logs, and background workers. Authorization requires continuous testing, auditing, lifecycle governance, and careful attention to privacy and least privilege.

Key Terms

Authentication: Verification of an identity.

Authorization: Decision about whether an identity may perform an action.

Access control: The mechanisms and rules used to grant or restrict access.

RBAC: A model that assigns permissions through organizational roles.

ABAC: A model that evaluates subject, resource, action, and environmental attributes.

Policy-based authorization: Authorization evaluated through explicit, governed policy rules.

Relationship-based authorization: Access determined by relationships between subjects and resources.

Least privilege: Granting only the access necessary for an approved purpose.

Privilege escalation: Obtaining authority beyond that originally granted.

Horizontal escalation: Accessing another user’s resources at a similar privilege level.

Vertical escalation: Performing actions reserved for a higher privilege level.

IDOR: Unauthorized access caused by failing to check permissions for a client-referenced object.

Tenant isolation: Preventing one organizational customer from accessing another’s data or operations.

Deny by default: Treating unapproved or uncertain requests as denied.

Separation of duties: Dividing sensitive responsibilities so one person cannot control the whole process.

Policy engine: A component that evaluates authorization rules and returns a decision.

Entitlement: A specific permission to perform an action on a resource.

Break-glass access: Exceptional, controlled emergency access with strong auditing.

Mass assignment: Unsafe binding of client-supplied fields to protected application attributes.

Auditability: The ability to reconstruct and review access decisions and administrative actions.

Review Questions

Short Answer Questions

1. How does authorization differ from authentication?

2. What is the difference between horizontal and vertical privilege escalation?

3. Why does a random object identifier not prevent IDOR?

4. State two characteristics of ABAC.

5. Why must background jobs carry explicit tenant context?

Long Answer Questions

1. Compare RBAC, ABAC, policy-based authorization, and relationship-based authorization, including suitable application contexts for each.

2. Explain a layered server-side authorization architecture for a REST web application.

3. Discuss the design requirements for tenant isolation in a shared-table SaaS application.

4. Explain how authorization governance, audit logging, access reviews, and separation of duties work together.

5. Describe a comprehensive authorization testing strategy for detecting IDOR and privilege escalation.

Analytical / Application Questions

1. An endpoint checks `project:read` but retrieves projects by ID without checking tenant membership. Identify the vulnerabilities and propose a corrected design.

2. A company allows employees to approve expenses only within departmental limits, but the requester must not approve their own expense. Which authorization model and attributes would you use, and why?

3. A policy service becomes temporarily unavailable during a high-value transaction. Analyze the security and availability trade-offs of fail-open, fail-closed, and carefully bounded cached decisions.

22. Multiple Choice Questions with Answers

Question 1

Which statement best represents an authorization decision?

A. It verifies that a password matches a stored hash.

B. It determines whether a subject may perform an action on a resource in context.

C. It encrypts a resource before transmission.

D. It assigns an IP address to a client.

Correct Answer: B. It determines whether a subject may perform an action on a resource in context.

Question 2

A user changes an invoice ID in an API URL and views another customer’s invoice. What is the primary weakness?

A. Cross-site scripting

B. IDOR combined with inadequate tenant authorization

C. Weak password hashing

D. Insufficient transport encryption

Correct Answer: B. IDOR combined with inadequate tenant authorization

Question 3

Which situation is most suitable for ABAC rather than simple RBAC?

A. Every employee in a fixed role receives identical permissions.

B. A user may approve an expense only when department, amount, and requester conditions are satisfied.

C. All authenticated users may read public documentation.

D. A system assigns one permission to every registered account.

Correct Answer: B. A user may approve an expense only when department, amount, and requester conditions are satisfied.

Question 4

Why is hiding an administrator button in the user interface insufficient security?

A. Interfaces cannot display text reliably.

B. Users may not understand the button label.

C. A caller can directly invoke the underlying server endpoint.

D. Buttons always expose passwords.

Correct Answer: C. A caller can directly invoke the underlying server endpoint.

Question 5

Which practice most directly protects a shared-table multi-tenant database?

A. Using sequential numeric identifiers only

B. Accepting tenant ID from a hidden form field

C. Applying trusted tenant scoping to every relevant query

D. Allowing all users to query the table and filtering in the browser

Correct Answer: C. Applying trusted tenant scoping to every relevant query

Question 6

A normal employee changes a request parameter from `role=user` to `role=admin` and becomes an administrator. Which design failure enabled this?

A. Mass assignment or reliance on client-controlled privilege data

B. Use of encrypted cookies

C. Excessive audit logging

D. Use of a policy engine

Correct Answer: A. Mass assignment or reliance on client-controlled privilege data

Question 7

Which authorization-testing case specifically tests horizontal privilege escalation?

A. An unauthenticated visitor requests a public page.

B. A manager accesses a management report.

C. User A attempts to read User B’s document in the same tenant.

D. A service renews an expired certificate.

Correct Answer: C. User A attempts to read User B’s document in the same tenant.

Question 8

What is the main purpose of separation of duties?

A. To make all users administrators temporarily

B. To ensure one person cannot control an entire sensitive process

C. To eliminate the need for authentication

D. To replace audit logs with encryption

Correct Answer: B. To ensure one person cannot control an entire sensitive process

Question 9

Which response is generally safest when a requested object is outside the caller’s authorized scope?

A. Return the entire object with a warning.

B. Return detailed database error information.

C. Deny access without unnecessarily revealing sensitive existence information.

D. Ask the client to provide a different tenant ID.

Correct Answer: C. Deny access without unnecessarily revealing sensitive existence information.

Question 10

What is a significant risk of long-lived cached authorization decisions?

A. They make passwords impossible to change.

B. They may preserve access after a permission has been revoked.

C. They prevent all audit logging.

D. They automatically expose source code.

Correct Answer: B. They may preserve access after a permission has been revoked.

Web Application Security and Secure Coding
Chapter 6 of 14