cybersecurity · intermediate · ~15 min · safe pentest lab
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.
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.
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.
n == 0) or NULL pointer -> always deny.n (corrupted/attacker-influenced length) -> deny, do not loop.object_id of 0 or a negative value that is not in the set -> deny.in_allowed_set(allowed, n, allowed[0]) -> 1
in_allowed_set(allowed, n, 999999) -> 0
in_allowed_set(NULL, n, 5) -> 0
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.
Returns int 1 if object_id is present in allowed[0..n-1], otherwise 0. No output is printed and no memory is modified.
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.
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;
}
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.
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.
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.