1

Concept

User-to-User Access

User-to-user access describes the case where two users have the same privilege level, but one can access resources belonging to the other.

No admin or higher-privilege role is involved. Both users may be ordinary, fully authenticated accounts:

Logged in as user 4521:

GET /api/messages/inbox
→ own inbox → allowed ✓

GET /api/messages/4522
→ another user's messages → should be denied ✗

A vulnerable server might perform only the authentication check:

Is user 4521 authenticated?
→ Yes ✓

Is user 4521 authorized to access messages for user 4522?
→ Never checked ✗

→ Data returned

The important distinction is:

Authentication
→ "Is this a valid logged-in user?"

Authorization
→ "Can this user access this specific resource?"

Because both accounts have the same role, a role-based check such as:

role == "user"

would pass for both users and would not prevent the attack.

The server needs an object-level relationship check:

Requester: user 4521
Resource: messages belonging to 4522
        ↓
Does user 4521 have permission to access them?
        ↓
No → 403 / equivalent denial

This is the classic horizontal authorization failure, commonly associated with IDOR and, in API security, BOLA (Broken Object Level Authorization).

2

Concept

IDOR

IDOR describes a vulnerability where an application exposes a direct reference to an internal object — such as an ID, filename, or other identifier — and fails to verify that the requester is authorized to access that specific object.

For example:

GET /invoices/download?id=88231
→ your invoice ✓

GET /invoices/download?id=88232
→ another user's invoice ✗

The important part is not simply that the attacker can change id. The vulnerability exists because the server trusts the changed reference without performing an object-level authorization check.

Conceptually:

id = 88232
        ↓
Find invoice 88232
        ↓
Does this user have permission to access it?
        ↓
Missing check
        ↓
Invoice returned

A vulnerable implementation might effectively do:

$invoice = Invoice::findOrFail($request->id);

return $invoice->download();

The server finds the requested invoice, but never establishes that the authenticated user is allowed to access it.

A safer approach scopes the resource to the requester:

$invoice = Invoice::where('id', $request->id)
                  ->where('owner_id', auth()->id())
                  ->firstOrFail();

The reference can appear in many places, not just a URL:

GET  /invoices/88232
POST /invoices/download   {"id": 88232}
GET  /files/report-88232.pdf
PATCH /api/orders/88232

In modern API-security terminology, this often falls under BOLA (Broken Object Level Authorization). BOLA is the broader authorization failure; IDOR is the classic implementation pattern where a directly referenced object can be accessed without the required authorization check.

3

Concept

Identifier Manipulation

Identifier manipulation is a testing technique, not a separate vulnerability class. The tester changes the identifiers supplied by the client and observes whether the application properly enforces authorization for the newly referenced resource.

For example:

Sequential IDs
→ /orders/1001
→ /orders/1002
→ /orders/1003
→ easy to enumerate

UUIDs
→ /orders/e4f9-...
→ harder to guess blindly
→ still accessible if the UUID is discovered elsewhere

A UUID therefore provides unpredictability, not authorization. If an attacker obtains a valid UUID from an email, shared link, API response, browser history, or another endpoint, the server still needs to verify that the requester is authorized to access that object.

The testing should also cover every location where an identifier can appear, not just URL paths:

URL path
→ /orders/4522

Query parameter
→ /orders?id=4522

JSON body
→ {"order_id": 4522}

Form field
→ order_id=4522

Header
→ X-Account-ID: 4522

Cookie
→ account_id=4522

The key is to identify which value the server actually uses to select the resource, then determine whether changing that value causes access to another user's object without an appropriate authorization decision.

If the server uses order_id from the body without applying the same authorization check, the protection on the URL parameter doesn't matter.

The testing principle is therefore:

Find every client-controlled object reference
        ↓
Change the reference
        ↓
Observe which resource the server operates on
        ↓
Verify authorization for that specific resource

An identifier being difficult to guess is not authorization. Every client-controlled reference that can select an object must be treated as untrusted input and independently authorized.

4

Concept

Cross-Account Access

Cross-account access occurs when manipulating an account, organization, or tenant identifier allows a user to access resources belonging to a different account or tenant.

For example:

GET /api/reports?account_id=771
→ your company's reports ✓

GET /api/reports?account_id=772
→ another company's entire dataset ✗

The vulnerable application may correctly authenticate the requester but trust the supplied account_id:

Authenticated user
        ↓
account_id = 772
        ↓
Fetch reports for account 772
        ↓
No check that user belongs to account 772
        ↓
Data returned

This is particularly dangerous in multi-tenant SaaS and B2B applications, where identifiers such as:

account_id
tenant_id
organization_id
customer_id

define a security boundary between completely separate customers.

Check must happen before allowing access to resources scoped to that account.

A secure request therefore needs to establish both levels where applicable:

Authenticated user
        ↓
Which account/tenant does this user belong to?
        ↓
Is the requested account authorized?
        ↓
Is the user authorized for this specific resource?
        ↓
Allow / Deny

The impact can be much greater than a single-object IDOR:

Single-object IDOR
→ one unauthorized invoice

Cross-account access
→ invoices
→ reports
→ users
→ files
→ settings
→ potentially an entire customer's dataset

In a multi-tenant system, the tenant boundary is itself an authorization boundary. Never trust a client-supplied account_id, tenant_id, or org_id to determine which customer's data the requester may access.

You've completed Authorization and Access Control

Great work — explore other topics to keep learning.