Chapter 2 · Practice 2 of 5
Map identifiers like sort columns to an allowlist
Why it matters
Placeholders work for values, not for table names, column names or sort direction. A sortable table that does ORDER BY ${req.query.sort} is injectable even when every value in the query is parameterized.
OWASP's advice is to keep those identifiers in code: map the user's choice to a known name and reject anything else. It calls user input in table or column names "a symptom of poor design". For sort direction, turn the input into a boolean and choose ASC or DESC yourself.
The same applies to:
- field names in a dynamic filter (
?filter[email]=) - JSON paths in document databases
- index or collection names
LIMITvalues, which should be parsed as integers and capped
Example
const SORTABLE = { newest: 'created_at', title: 'title', price: 'price_cents' } as const;
export function orderBy(sort: unknown, dir: unknown) {
const column = SORTABLE[sort as keyof typeof SORTABLE] ?? 'created_at';
const direction = dir === 'asc' ? 'ASC' : 'DESC';
return `ORDER BY ${column} ${direction}`; // both values come from code
}