FlawOpen

flawopen.com/JavaScript 中的 SQL 注入/Knex.js

Knex.js 能防止 SQL 注入吗?

参考指南
简答

把 SQL 想成印好的表格:binding 将输入放入值栏;字符串插值则把它粘进指令。Knex 查询构建器和knex.raw(sql, bindings)会分离值,但表名和列名属于 SQL 结构,必须通过可信允许列表选择。

??是 Knex 的 raw 标识符占位符,会引用标识符而非绑定数据值;仍需用允许列表限制可选项。
安全 — 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]);

Knex bindings 如何保护值

.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 字符串会使其成为 SQL 源码的一部分。请检查每个 raw 调用及输入。

使用 ?? 后可以接受用户选择的任意列吗?

??是 Knex 的 raw 标识符占位符,会引用标识符而非绑定数据值;仍需用允许列表限制可选项。

参考资料

另请参阅 SQL 注入(CWE-89):原理与各编程语言的防护方法 — flawopen.com MyBatis ${} 与 #{}:防止 SQL Injection MySQLi 与 PDO:安全使用预处理语句 NHibernate 能防止 SQL 注入吗? MongoDB NoSQL 注入:安全查询过滤器 数字 ID 使用字符串拼接也会导致 SQL 注入吗? 什么是参数化查询?