Node.jsで安全にSQLクエリを生成する方法

Node.jsを使用してSQLクエリを生成する際、適切な方法で実装しなければSQLインジェクションのリスクが高まります。
本記事では、プレースホルダーを活用し、安全かつ柔軟なWHERE句を作成する方法を詳しく解説します。

SQLクエリの動的生成における課題

動的なSQLクエリを作成する場合、以下のような課題が発生することがあります。

  • SQLインジェクションのリスク
  • プレースホルダーの管理
  • 複数の条件を適切に組み合わせる
  • NULL値や配列の処理

特に、IN句を使う場合に適切な実装を行わないと、意図しないデータ構造になり、エラーの原因となります。

WHERE句を安全に生成する関数

以下の関数は、Node.jsSQLクエリWHERE句を安全に生成するものです。

/**
 * WHERE句を作成する汎用関数
 * @param {string} tableName - テーブル名
 * @param {string} field - フィールド名
 * @param {string} operator - 比較演算子
 * @param {Array|string} addField - 追加のフィールド
 * @param {Object} queryParams - クエリパラメータ
 * @param {number} currentIndex - プレースホルダーのインデックス
 * @returns {Object|null} - クエリテキストとインデックスを含むオブジェクト
 */
const whereQuery = (tableName, field, operator, addField = [], queryParams, currentIndex) => {
  if (operator === 'IS NULL' || operator === 'IS NOT NULL') {
    return { text: `(${tableName}.${field} ${operator})` };
  }

  const value = queryParams[field];
  if (value === undefined || value === null || value === '') {
    return null;
  }

  if (operator === 'LIKE') {
    return {
      text: `(${tableName}.${field} ${operator} $${currentIndex})`,
      values: [`%${value}%`],
      index: currentIndex + 1
    };
  }

  if (operator === 'BETWEEN' && Array.isArray(addField) && addField.length === 2) {
    const startValue = queryParams[addField[0]];
    const endValue = queryParams[addField[1]];
    return (startValue && endValue) ? {
      text: `(${tableName}.${field} ${operator} $${currentIndex} AND $${currentIndex + 1})`,
      values: [startValue, endValue],
      index: currentIndex + 2
    } : null;
  }

  if (operator === 'IN' && Array.isArray(addField) && addField.length > 0) {
    const values = queryParams[addField];
    if (Array.isArray(values) && values.length > 0) {
      const placeholders = values.map((_, index) => `$${currentIndex + index}`).join(', ');
      return {
        text: `(${tableName}.${field} ${operator} (${placeholders}))`,
        values: values,
        index: currentIndex + values.length
      };
    }
    return null;
  }

  return {
    text: `(${tableName}.${field} ${operator} $${currentIndex})`,
    values: [value],
    index: currentIndex + 1
  };
};

IN句を扱う際のポイント

誤った実装例(多次元配列の問題)

let queryValues = [];
if (wheres.length > 0) {
  wheres.forEach(condition => {
    if (condition.text) {
      queryText.push(condition.text);
      if (condition.value !== undefined) {
        queryValues.push(condition.value);
      }
      if (condition.values !== undefined) {
        queryValues.push(condition.values); // ここで多次元配列の可能性がある
      }
    }
  });
}

上記のコードでは、queryValuesに配列をそのまま追加してしまうため、多次元配列になる可能性があります。

修正後の正しい実装

let queryValues = [];
if (wheres.length > 0) {
  wheres.forEach(condition => {
    if (condition.text) {
      queryText.push(condition.text);
      if (condition.value !== undefined) {
        queryValues.push(condition.value);
      }
      if (Array.isArray(condition.values) && condition.values.length > 0) {
        queryValues.push(...condition.values); // スプレッド構文で展開し追加
      }
    }
  });
}

この修正により、多次元配列になることを防ぎ、期待どおりのフラットな配列が得られます。

まとめ

  • Node.jsSQLクエリを動的に生成する際は、プレースホルダーを活用して安全に実装する。
  • IN句を扱う場合、多次元配列が発生しないよう、スプレッド構文を使用する。
  • Array.isArray()length > 0を組み合わせて適切なデータチェックを行う。
  • オプショナルチェーン(?.)を活用すると、簡潔なコードが書ける。

このような工夫をすることで、安全で柔軟なSQLクエリを生成でき、バグの少ないシステム開発が可能になります。ぜひ、実装の際に参考にしてください。


Amazonベストセラー

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA