Security Glossary

Horizontal Privilege Escalation

An access control vulnerability where a user reaches a peer's resources at the same privilege level, usually because an object is referenced by a client-supplied ID with no ownership check.

Horizontal Privilege Escalation occurs when a user accesses the resources or actions of a peer at the same privilege level, for example one customer reading another customer's order. The privilege level does not change; only the owner of the data does. That is what separates it from vertical escalation, where the attacker gains a higher privilege level.

Relationship to IDOR

Horizontal escalation describes the outcome (reaching a peer's data). Insecure Direct Object Reference (IDOR) describes the mechanism that usually enables it: the app references an object by a client-supplied identifier and never checks that the requester owns that object. Most horizontal escalation is an IDOR; but not every IDOR is horizontal (an IDOR that exposes an admin-only object crosses into vertical escalation).

How It Works

The application authenticates the user but authorizes the action without binding it to the specific resource. It confirms you are logged in, then trusts the identifier you sent to pick whose record to return.

// Alice is authenticated and views her own profile
GET /profile?user_id=100      (Alice's ID)

// Alice swaps in Bob's ID, same privilege level
GET /profile?user_id=101      (Bob's ID)

// App checks:  "Is someone logged in?"           -- YES
// App skips:   "Does user 100 own resource 101?" -- NEVER CHECKED
// Result: Alice reads Bob's profile

Common Scenarios

  • Reading another user's private data (profile, documents, messages)
  • Modifying another user's settings or cancelling their order
  • Downloading invoices or transactions belonging to a peer account

Prevention

  • Scope every query to the authenticated user rather than checking authentication alone: current_user.orders.find(params[:id]), not Order.find(params[:id])
  • Derive the owner from the session, never from a client-supplied user_id
  • Enforce ownership at the data layer (row-level security) so a missing controller check cannot leak a peer's row

See Also