cybersecurity · intermediate · ~15 min · safe pentest lab

BFLA: Rank-Based Function Authorization

Enforce server-side function-level authorization with a deny-by-default rank check, validating both the caller's role and the required role before comparing them, so that invalid or forged privilege values are rejected.

Challenge

Broken Function-Level Authorization (BFLA)

An API exposes privileged actions (delete users, change billing, promote accounts). Each action declares a required role. The insecure assumption developers make is that hiding a button or checking the role only on the client is enough — but an attacker calls the endpoint directly and the server must decide, on its own, whether the caller is allowed.

Roles are ranked 0..4: guest(0), user(1), mod(2), admin(3), superadmin(4). Your gatekeeper receives the caller's user_role and the endpoint's required_role and must return 1 only when access is granted.

Task: Implement authorize_function. Return 1 iff both user_role and required_role are valid ranks in 0..4 AND user_role >= required_role. Otherwise return 0.

The asset: privileged API functions. The threat: a low-rank (or forged) caller invoking a high-rank action. Deny by default — any out-of-range, negative, or oversized role value must be rejected, never trusted or compared as-is. A forged role like 99 must not be treated as "higher than admin".

Edge cases

  • Exact match (user_role == required_role) is allowed.
  • Boundaries 0 and 4 are valid.
  • Negative or >4 values on either argument mean deny.

Example

  • authorize_function(3, 2) -> 1 (admin may use a mod endpoint)
  • authorize_function(1, 3) -> 0 (user denied an admin endpoint)
  • authorize_function(99, 0) -> 0 (forged role rejected)

Input format

Two ints: user_role (the caller's rank) and required_role (the rank the endpoint demands). Valid ranks are 0..4; any other value is attacker-controlled/invalid input.

Output format

Return int 1 if access is granted, 0 if denied.

Constraints

Valid roles are integers 0..4 inclusive. Either argument may be any int (including negative or large values) and must be validated before use. No I/O, allocation, or global state — pure decision function.

Starter code

int authorize_function(int user_role, int required_role){
    /* TODO: enforce deny-by-default rank authorization.
       - reject any role outside 0..4 (negative, or > 4)
       - grant only when user_role >= required_role
       Replace this insecure stub. */
    (void)user_role; (void)required_role;
    return -1; /* insecure placeholder: fails the tests */
}

Common mistakes

Comparing user_role >= required_role without first bounds-checking either value, so a forged role like 99 passes as super-admin; validating only user_role but not required_role; using > instead of >= and wrongly denying exact matches; treating negative numbers as valid low ranks; returning the boolean of the comparison before rejecting out-of-range inputs (fail-open instead of deny-by-default).

Edge cases to handle

Exact-rank match (user_role == required_role) is allowed; lowest rank 0 and highest rank 4 are both valid; negative user_role or required_role is denied; a role above 4 (e.g. a forged 99) is denied and must NOT be read as outranking admin; if the required_role itself is out of range the request is denied even for a superadmin caller; both-invalid inputs are denied without any numeric comparison.

Background lessons

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