FlawOpen

flawopen.com/SQL-инъекции в JavaScript/Knex.js

Защищает ли Knex.js от SQL-инъекций?

Справочное руководство
Краткий ответ

Представьте SQL как бланк: binding помещает ввод в поле значения, а интерполяция вклеивает его в инструкции. Query builder Knex и knex.raw(sql, bindings) отделяют значения; имена таблиц и столбцов — структура SQL, для них нужен доверенный список.

?? — raw-маркер идентификатора Knex. Он экранирует имя, но не разрешает выбор; используйте список допустимых столбцов.
БЕЗОПАСНО — bindings и список
const query = knex.raw(
  `SELECT * FROM users WHERE id = ${userId}`
);
НЕБЕЗОПАСНО — SQL собран из ввода
// Ввод конкатенируется со строкой SQL.
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"
  );

// Отделяйте значение от синтаксиса SQL.
knex.raw("SELECT * FROM users WHERE id = ?", [userId]);

Как bindings Knex защищают значения

Методы .where(), .insert() и .select() отделяют значения от SQL. В raw передавайте значения через bindings: knex.raw("... = ?", [value]). ? обозначает значение, ?? — идентификатор. Не вставляйте ввод интерполяцией в SQL-строку.

Для динамических идентификаторов нужен список

Binding не разрешает произвольные столбцы, таблицы, сортировки, операторы или фрагменты SQL. ?? экранирует идентификатор, но не решает, разрешен ли такой выбор. Сопоставляйте ввод с фиксированным списком до .orderBy(); ключевые слова и операторы задавайте в коде.

Как проверять raw-запросы

Найдите все вызовы knex.raw() и проверьте SQL вместе с bindings. Значения должны передаваться через bindings, а динамические идентификаторы — браться из небольшого списка. Raw-запрос не обязательно уязвим: все зависит от формирования SQL и ввода.

rg -n 'knex\.raw\(' -g '*.js' -g '*.ts' -g '*.mjs' -g '*.cjs' .

Вопросы и ответы

Защищает ли knex.raw() от SQL-инъекций сам по себе?

Нет. Передавайте недоверенные значения через bindings, например knex.raw("... = ?", [value]). Интерполяция превращает значение в часть SQL-кода. Проверяйте каждый raw-вызов и его вводы.

Можно ли с ?? принимать любой выбранный пользователем столбец?

?? — raw-маркер идентификатора Knex. Он экранирует имя, но не разрешает выбор; используйте список допустимых столбцов.

Источники

См. также SQL-инъекции (CWE-89): как они работают и как их предотвратить в любом языке — flawopen.com MyBatis ${} и #{}: защита от SQL Injection MySQLi и PDO: безопасные подготовленные запросы Защищает ли NHibernate от SQL-инъекций? NoSQL-инъекция в MongoDB: безопасные фильтры SQL Injection при числовых ID: риск? Что такое параметризованный запрос?