cybersecurity · intermediate · ~15 min · safe pentest lab

BFLA: Role-Action Allowlist Check

Enforce function-level authorization server-side with an explicit deny-by-default allowlist, and safely reject NULL/empty/unknown attacker-controlled input before acting on it.

Challenge

Broken Function Level Authorization (BFLA) happens when an API decides who you are but forgets to check whether your role is allowed to perform this specific action. A low-privileged user (role 1) who can only read sends a crafted request asking to delete, and a fail-open endpoint happily performs it because it never consulted an authorization policy.

The protected asset here is a fixed permission policy baked into the grading harness. Your job is to be the server-side authorization gate. Implement action_allowed(role, action) so it consults a fixed allowlist and returns 1 only when that exact role is granted that exact action, and 0 otherwise.

The policy the harness enforces:

  • role 1 -> read
  • role 2 -> read, write
  • role 3 -> read, write, delete

Deny by default. Any unknown role, any action not granted to that role, and any invalid input (a NULL or empty action string) must return 0. Do not dereference the action pointer before checking it for NULL, and do exact string matching only (READ is not read).

Example

  • action_allowed(1, "read") -> 1
  • action_allowed(1, "delete") -> 0
  • action_allowed(3, "delete") -> 1
  • action_allowed(9, "read") -> 0

Input format

Two arguments: int role (an integer role id; only 1, 2, 3 are known roles) and const char *action (a NUL-terminated string naming the requested action, e.g. "read", "write", "delete"; may be NULL or empty).

Output format

Return int 1 if the given role is explicitly granted the given action by the fixed allowlist, otherwise return 0.

Constraints

C11. No dynamic allocation, no I/O, no global mutable state. The action pointer may be NULL or point to an empty string and must be handled safely before any dereference. Matching is exact and case-sensitive. Deny by default: anything not explicitly on the allowlist returns 0.

Starter code

int action_allowed(int role, const char *action){
    /* TODO: consult a FIXED allowlist and return 1 only when this exact
       role is granted this exact action; return 0 for everything else
       (unknown role, ungranted action, NULL/empty action).
       This stub fails open and is INSECURE — replace it. */
    (void)role; (void)action;
    return -1;
}

Common mistakes

Failing open (returning 1 by default and only denying a few known-bad cases) instead of denying by default; dereferencing action with strcmp before checking it for NULL, causing a crash; using strncmp/prefix or case-insensitive matching that lets "read_all" or "READ" slip through; checking only the action and ignoring the role, so any role can do anything (the exact BFLA flaw); assuming a higher role's permissions imply a lower role is fine.

Edge cases to handle

action is NULL; action is the empty string ""; unknown action for an otherwise valid role (e.g. role 3 asking to "drop"); action valid for a higher role but not this one (role 1 asking to "delete"); unknown role id (9); negative role (-1); role 0; case-variant action ("READ") that must not match "read".

Background lessons

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