flawopen.com/SQL Injection in JavaScript/Knex.js
Is Knex.js safe from SQL injection?
Think of SQL as a printed form: a binding puts input in a value field; string interpolation pastes it into the form’s instructions. Knex’s query builder and knex.raw(sql, bindings) keep values separate, but table and column names are SQL structure and need a trusted allowlist.
// Input is concatenated into SQL source.
const query = knex.raw(
`SELECT * FROM users WHERE id = ${userId}`
);
// Keep the value separate from SQL syntax. const allowedSortColumns = new Set(["created_at", "name"]); const rows = await knex("users") .where("id", userId) .orderBy( allowedSortColumns.has(requestedSort) ? requestedSort : "created_at", requestedDirection === "asc" ? "asc" : "desc" ); // Raw: ? is a value; ?? is an identifier. knex.raw("SELECT * FROM users WHERE id = ?", [userId]);
How Knex bindings protect values
Knex query-builder methods such as .where(), .insert(), and .select() separate values from generated SQL. For raw expressions, pass values as bindings: knex.raw("... = ?", [value]). Knex documents ? as a value placeholder and ?? as an identifier placeholder. Do not interpolate input into a SQL template string.
Dynamic identifiers need an allowlist
A binding does not authorize a user-selected column, table, sort direction, operator, or SQL fragment. Knex can quote an identifier with ??, but quoting does not decide whether the caller should be allowed to choose it. Map user options to a fixed allowlist before passing them to .orderBy() or identifier bindings. Keep SQL keywords and operators fixed in code.
How to audit raw queries
Search for every knex.raw() call and review its SQL string and bindings together. Confirm data values use bindings and any dynamic identifier comes from a small allowlist. A raw query is not automatically vulnerable; safety depends on how its SQL text and inputs are constructed.
rg -n 'knex\.raw\(' -g '*.js' -g '*.ts' -g '*.mjs' -g '*.cjs' .FAQ
No. Raw SQL can safely handle untrusted values when you use bindings, such as knex.raw("... = ?", [value]). Interpolating a value into the SQL string makes it part of the SQL source. Review each raw call and its inputs.
?? is Knex’s raw identifier placeholder; it quotes an identifier instead of binding a data value. Still map user choices to an allowlist so callers can select only the columns the application intends to expose.
References
- Knex.js documentation — Raw queries and bindings
- Knex.js documentation — Query builder
- flawopen.com — SQL Injection in JavaScript