FlawOpen

flawopen.com/JavaScriptのSQL Injection/Knex.js

Knex.jsはSQL Injectionを防げますか?

リファレンスガイド
要点

SQLを印刷済みフォームにたとえると、bindingは入力を値欄に入れ、文字列補間は指示文に貼り付けます。Knexのquery builderと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])。?は値、??は識別子です。入力をテンプレート文字列に補間しないでください。

動的な識別子には許可リストが必要

bindingはユーザーが選んだ列、テーブル、並び順、演算子、SQL断片を許可しません。??は識別子を引用できますが、選択権限は判断しません。.orderBy()に渡す前に固定の許可リストへ対応付け、SQLキーワードと演算子はコードで固定します。

rawクエリの監査方法

すべてのknex.raw()を検索し、SQL文字列とbindingsを合わせて確認します。値はbindings、動的識別子は小さな許可リストから取得します。rawが自動的に脆弱なのではなく、SQLと入力の組み立て方で決まります。

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

よくある質問

knex.raw()だけでSQL Injectionを防げますか?

いいえ。knex.raw("... = ?", [value])のようにbindingsを使えば未信頼値を安全に扱えます。値をSQL文字列に補間するとSQLソースの一部になります。各raw呼び出しを確認してください。

??を使えばユーザーが選んだ任意の列を受け入れられますか?

??はKnexのraw識別子プレースホルダーで、値ではなく識別子を引用します。許可リストで選択肢を制限してください。

参考資料

関連項目 SQL インジェクション(CWE-89)の仕組みと各言語での防ぎ方 — flawopen.com MyBatisの${}と#{}:SQL Injectionを防ぐ MySQLiとPDO:安全なPrepared Statement NHibernate は SQL Injection を防げますか? MongoDB NoSQL Injection:安全なクエリフィルター 数値 ID でも文字列連結で SQL Injection が起きますか? パラメータ化クエリとは何か