cybersecurity · intermediate · ~15 min · safe pentest lab

Server-Side Access Control: Stop the IDOR

Implement a deny-by-default server-side authorization check that defeats horizontal IDOR: authorize only on an explicit admin role or a validated owner match, and validate attacker-influenced identifiers before trusting them.

Challenge

An Insecure Direct Object Reference (IDOR) is one of the most common real-world access-control bugs. A GET /api/resource/{owner} endpoint returns a record based on an id the client supplies. If the server only checks that some user is logged in and trusts the id, any authenticated user can swap the id and read another user's data — a horizontal privilege escalation.

The asset here is a per-user resource (think an invoice, message, or profile). The threat is an authenticated attacker tampering with the resource_owner id in the request. The insecure assumption is "the client would only ask for its own id".

Your job is to implement the server-side authorization decision as a pure function. Return 1 (allow) only when the request is legitimately authorized, and 0 (deny) otherwise. Enforce deny-by-default: authorize only on an admin role OR a genuine owner match, and reject invalid identifiers before trusting them.

Example

  • can_view_resource(10, 10, 1) -> 1 (owner viewing their own resource)
  • can_view_resource(10, 20, 1) -> 0 (IDOR attempt: not owner, not admin)
  • can_view_resource(99, 20, 3) -> 1 (admin may view any valid resource)

Edge cases you must handle: non-positive ids (0 or negative) are always invalid and must be denied even if caller_id == resource_owner, and even for an admin role.

Input format

Three ints: caller_id (the authenticated caller's identity, server-trusted), resource_owner (the id of the record's owner, effectively attacker-controllable), and caller_role (an integer privilege level; admin is >= 3). Valid ids are strictly positive.

Output format

Return int 1 if the caller is authorized to view the resource, otherwise 0.

Constraints

Valid identifiers are strictly > 0. Roles are small non-negative ints; admin threshold is role >= 3. The function must be pure (no I/O, no globals) and must not authorize based solely on a client-supplied id. Deny by default.

Starter code

int can_view_resource(int caller_id, int resource_owner, int caller_role){
    /* TODO: implement server-side access control.
       Return 1 only if the caller is admin (role >= 3) OR is the
       validated owner (caller_id == resource_owner, both > 0).
       Deny by default. */
    (void)resource_owner; (void)caller_role;
    return caller_id > 0; /* INSECURE placeholder: trusts client id, remove this */
}

Common mistakes

Returning 1 whenever the user is authenticated (ignoring ownership); comparing ids without first rejecting non-positive/invalid values, so caller_id==resource_owner==0 slips through; using role > 3 instead of role >= 3 and locking out real admins; short-circuiting on the admin check before validating ids so a malformed request with an admin token is honored.

Edge cases to handle

caller_id == resource_owner but both are 0 (invalid, deny); negative spoofed ids (deny); admin role but invalid ids (deny — validate first); role exactly 3 (admin, allow) vs role 2 (not admin, deny); non-owner authenticated user requesting another owner's resource (deny).

Background lessons

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.