cybersecurity · intermediate · ~15 min · safe pentest lab

BOLA Guard: Allowed-Object Set Check

Enforce object-level authorization defensively: check attacker-controlled identifiers against an explicit allow-list, deny by default, and bounds-check the length before iterating so a corrupted or hostile n can never cause an out-of-bounds read.

Challenge

Broken Object Level Authorization (BOLA / IDOR)

A BOLA vulnerability happens when an API endpoint accepts an object_id from the request and returns that object without checking that the caller is actually allowed to see it. An attacker simply changes /orders/1001 to /orders/1002 and reads someone else's data. This is the #1 API security risk in the OWASP API Security Top 10.

The asset is a per-caller authorization set: a small array allowed[] of the object IDs this specific user is permitted to touch. The threat is an attacker supplying an arbitrary object_id (including IDs they were never granted, negatives, or zero) and having the endpoint trust it. The insecure assumption is "if the ID looks valid, serve it" — instead you must deny by default and grant access only when the ID is explicitly present in the allowed set.

Your task

Implement in_allowed_set so it returns 1 iff object_id is one of the n entries in allowed[], and 0 otherwise. Do a bounds-safe linear scan: never read past allowed[n), and treat a NULL pointer, n == 0, or a negative n as "no objects allowed" (return 0). The array is a fixed in-harness buffer; you operate only on it, never on a live target.

Edge cases

  • Empty set (n == 0) or NULL pointer -> always deny.
  • Negative n (corrupted/attacker-influenced length) -> deny, do not loop.
  • object_id of 0 or a negative value that is not in the set -> deny.
  • The matching ID may be the first, middle, or last element.

Example

in_allowed_set(allowed, n, allowed[0]) -> 1
in_allowed_set(allowed, n, 999999)     -> 0
in_allowed_set(NULL, n, 5)             -> 0

Input format

A read-only pointer allowed to an array of n ints (the caller's permitted object IDs), the count n (n >= 0; may be passed as a hostile value), and a single object_id to authorize.

Output format

Returns int 1 if object_id is present in allowed[0..n-1], otherwise 0. No output is printed and no memory is modified.

Constraints

C11, freestanding function (no I/O, no allocation). Must not read past allowed[n). Must handle allowed == NULL, n == 0, and n < 0 without crashing. Treat allowed as read-only (const). Deny by default.

Starter code

int in_allowed_set(const int *allowed, int n, int object_id)
{
    /* TODO: implement a bounds-safe linear scan that denies by default.
       Right now this insecure stub trusts the caller and grants everything. */
    (void)allowed; (void)n; (void)object_id;
    return 1;
}

Common mistakes

Returning 1 for any input (trusting the caller); looping with i <= n and reading allowed[n] (off-by-one out-of-bounds read); using an unsigned loop counter so a negative n wraps to a huge value; skipping the NULL / n<=0 guards and dereferencing a null or walking a bogus length; returning the matched value or index instead of a strict 0/1.

Edge cases to handle

n == 0 (empty allow-list) must deny; allowed == NULL must deny; negative n must deny without looping; object_id == 0 or negative not in the set must deny; match at the first, middle, and last index must all be found; a large valid ID in the set must be granted.

Background lessons

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