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識別子プレースホルダーで、値ではなく識別子を引用します。許可リストで選択肢を制限してください。