← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

Authorisation

Security PHP 7.0+ Intermediate
debt(d9/e5/b7/t7)
d9 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'silent in production until users hit it' (d9). Missing authorisation checks produce no error — the endpoint returns data successfully. No detection_hints provided; SAST tools like Psalm/Phpstan generally cannot detect missing authorisation logic since they don't know which resources require ownership checks. Typically only discovered via penetration testing or breach.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). While quick_fix shows a one-line $this->authorize() call per action, retrofitting authorisation properly means auditing every controller action, writing policies/voters, and adding tests — cross-cutting work within the controller layer.

b7 Burden Structural debt — long-term weight of choosing wrong

Closest to 'strong gravitational pull' (b7). applies_to spans web and cli contexts; authorisation policies become load-bearing across the entire application — every new feature must integrate with the policy/voter system, and changes to the permission model ripple through controllers, tests, and UI.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception (UI hiding equals authorisation) is a catastrophic mental model error that contradicts how server-side security works. Developers from frontend backgrounds routinely assume hidden = protected, leading directly to IDOR — OWASP A01.

About DEBT scoring →

Also Known As

authz authorisation access control permissions check policy Laravel policy Symfony voter

TL;DR

The process of determining what an authenticated user is permitted to do — checking permissions, roles, or policies before allowing access to a resource or action.

Explanation

Authorisation (authz) answers 'what can this user do?' — distinct from authentication ('who is this user?'). After a user authenticates, every protected action must check whether that user has permission. Authorisation models: RBAC (roles with permissions assigned to users — the most common PHP pattern); ABAC (attribute-based, evaluates resource + user + environment attributes); ACL (per-resource permission lists — flexible but hard to manage at scale). In PHP frameworks: Laravel uses Gates (closure-based checks) and Policies (model-specific classes with methods like view, create, update, delete — automatically mapped to controller actions via $this->authorize()); Symfony uses Voters (classes that implement the vote() method for granular attribute-based decisions). The most critical authorisation rule: check on every request, never rely on hiding UI elements as the only control — a hidden button is not an access control.

How It's Exploited

Authorisation bypass occurs when an attacker circumvents permission checks to perform actions they shouldn't be allowed to. Common exploitation patterns: (1) Direct object reference — accessing /user/123/profile when authenticated as user 456, if the application only checks 'is user logged in?' not 'does this user own profile 123?'; (2) Parameter tampering — modifying role or permission fields in requests (e.g., changing role=user to role=admin in a POST body or JWT token, if validation is weak); (3) Insecure direct object reference (IDOR) — enumeration of resource IDs to access others' data; (4) Horizontal privilege escalation — accessing peer resources (another user's invoice); (5) Vertical privilege escalation — jumping from user to admin role; (6) Race conditions — exploiting time gaps between authentication and authorisation checks; (7) Relying on UI-only controls — backend doesn't re-verify permissions on hidden actions, so direct API calls bypass the check. Example: a Laravel controller calls $this->authorize('update', $post) in the view but the API endpoint doesn't — an attacker posts directly to the endpoint. Prevention: always authorise on the server side, use framework-native checks (Laravel Policies, Symfony Voters) consistently, verify permissions for every state-changing request, and test with accounts lacking expected permissions.

Watch Out

Developers often check authorisation at the UI layer (hiding buttons or forms) but forget to enforce it at the API/action layer — an attacker can bypass the hidden UI entirely and call the endpoint directly, so permission checks must live in your controller, policy, or service, never rely on frontend filtering alone.

Common Misconception

Hiding a button or menu item in the UI is sufficient authorisation. UI-level hiding is cosmetic — any user can call the underlying route directly. Authorisation must be enforced server-side on every request, regardless of what the UI shows. A user who manually calls DELETE /posts/123 must be checked server-side even if the delete button was hidden from them in the frontend.

Why It Matters

Missing authorisation checks are the second most common web vulnerability (OWASP A01:2021 — Broken Access Control). A PHP controller action that fetches a resource without checking whether the authenticated user owns it allows any logged-in user to access any other user's data simply by changing the ID in the URL. This is Insecure Direct Object Reference (IDOR) — trivial to exploit, devastating in impact, and completely preventable with one authorisation check per action.

Common Mistakes

  • Checking authorisation only in the UI layer — always enforce server-side on every request.
  • Using the authenticated user's ID from user input instead of the session — always read the user ID from the session, never from POST/GET parameters.
  • Missing authorisation on API endpoints when the web routes are protected — both must be checked independently.
  • Not testing authorisation — write tests that verify a user cannot access another user's resources.

Avoid When

  • Conflating authorisation with authentication — checking 'who is this?' instead of 'what can they do?' solves only half the security problem.
  • Using authorisation checks as a substitute for input validation — a user with permission to update a record still cannot be trusted with malformed or malicious data.
  • Implementing authorisation solely on the client side or UI layer — hidden buttons and disabled fields are not access controls and can be bypassed by direct API calls.
  • Treating authorisation as a one-time setup rather than a per-request enforcement — permissions can change, tokens can be compromised, and every protected action must re-verify authorization.

When To Use

  • A user successfully logs in to your app — you must authorise every subsequent request (view a profile, edit a post, delete a comment) before executing it, not just check if they're logged in.
  • You have different user roles (admin, editor, viewer) with distinct capabilities — use RBAC to assign permissions to roles and check them before allowing actions like publishing or deleting content.
  • A resource has sensitive data that some authenticated users should never access (a customer sees only their own invoices, not others') — implement resource-level authorisation checks in your business logic or ORM policies.
  • Your app integrates external APIs or microservices — ensure the authenticated user token also carries authorisation claims (scopes, roles) so downstream services can independently verify permissions before responding.

Code Examples

✗ Vulnerable
// Missing authorisation — any user can view any order
class OrderController {
    public function show(int $id): array {
        return Order::findOrFail($id)->toArray();
        // User 1 can access /orders/999 (belongs to user 2)
    }
}
✓ Fixed
// Authorisation check before returning data
class OrderController {
    public function show(Order $order): array {
        // Laravel Policy — checks order->user_id === auth()->id()
        $this->authorize('view', $order);
        return $order->toArray();
    }
}

// OrderPolicy
class OrderPolicy {
    public function view(User $user, Order $order): bool {
        return $user->id === $order->user_id
            || $user->hasRole('admin');
    }
}

Added 23 Mar 2026
Edited 22 Jun 2026
Views 61
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 1 ping S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 1 ping F 1 ping S 0 pings S 2 pings M 1 ping T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 2 pings T 0 pings W
No pings yet today
Google 2
Amazonbot 9 Perplexity 8 Google 6 SEMrush 5 ChatGPT 4 Ahrefs 4 Scrapy 3 Claude 2 Bing 2 Meta AI 1 PetalBot 1
crawler 41 crawler_json 4
DEV INTEL Tools & Severity
🔴 Critical ⚙ Fix effort: Low
⚡ Quick Fix
In every controller action: $this->authorize('view', $resource) in Laravel, or denyAccessUnlessGranted('view', $resource) in Symfony — before returning any data
📦 Applies To
PHP 7.0+ web cli
🔗 Prerequisites


✓ schema.org compliant