cybersecurity · beginner · ~15 min · safe pentest lab
Understand why identifiers (column/table names) cannot be protected by bind parameters, and implement the correct defense: an exact-match, deny-by-default allowlist that maps untrusted input onto a fixed set of known-safe identifiers.
User-supplied ORDER BY columns are a classic SQL injection sink. Bind parameters (?) protect values in a WHERE clause, but they cannot parameterize an identifier like a column or table name. So developers often paste the user's requested sort column straight into the query string: ... ORDER BY " + col. An attacker sends col = "id; DROP TABLE users;--" or a blind-injection boolean payload, and the database happily executes it.
The asset here is the query itself: the sort column is attacker-controlled text and the threat is arbitrary SQL execution. The insecure assumption is "the column name is harmless because it's just a field name."
Implement in_sort_allowlist(const char *col). Return 1 only if col exactly matches one of a fixed, hard-coded set of sortable columns: id, name, email, created_at, updated_at. Return 0 for everything else. This is the safe pattern: deny by default, and only map user input onto a known-good identifier you control. The caller uses the boolean to decide whether the requested column is allowed into the query at all.
NULL must return 0, never dereference it."ID", "id_", "name " (trailing space), and any injection payload built on a valid prefix must all be rejected.A single argument: const char *col, the user-requested sort column. It may be NULL, empty, a valid column name, or a malicious string.
Return an int: 1 if col is exactly one of the five allowlisted columns, otherwise 0.
C11. The harness provides all needed includes (string.h etc.). Do not read files, network, or any live target — operate only on the fixed allowlist baked into your function. Never dereference a NULL pointer. Comparisons must be exact (no prefix/substring/case-insensitive matching).
int in_sort_allowlist(const char *col){
/* TODO: only accept columns from the fixed allowlist; reject everything else. */
(void)col;
return -1;
}
Using strncmp or a prefix check so "id_evil" or "id; ..." slips through; case-insensitive comparison; forgetting the NULL check and crashing; returning a non-boolean (e.g. the strcmp result) so callers see a nonzero for a mismatch; blocklisting bad keywords instead of allowlisting good columns (blocklists are always incomplete).
NULL pointer (return 0, no deref); empty string; exact-but-wrong case ("ID"); valid column with trailing/leading whitespace ("name "); a valid prefix extended into an injection payload ("id; DROP TABLE users;--"); a column that merely starts like an allowed one ("id_").
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.