node-mssqlのクエリでのSQLインジェクション

node-mssqlのクエリでのSQLインジェクション

説明

文字列連結やテンプレートリテラルで外部入力をSQLに直接組み込むと、攻撃者がクエリを変更できる可能性があります。不正な閲覧や変更につながり、影響範囲はデータベースアカウントの権限と設定に左右されます。

想定される影響

  • パスワードなどの機密情報が漏えいするおそれがあります。
  • データを不正に追加、変更、削除される可能性があります。
  • 不正なクエリでデータベースやアプリケーションが停止するおそれがあります。

対処方法

  • 利用者の入力をSQLへ直接挿入せず、パラメーターとしてバインドしてください。
  • 入力の型、長さ、許可する文字を検証してください。
  • 値としてバインドできない動的なテーブル名や列名は、固定の許可リストから選んでください。

例

変更前

javascript
const express = require('express');
const mssql = require('mssql');

app.get('/user', async (req, res) => {
  const id = req.query.id;
  const pool = mssql.connect(/* config */);
  const request = (await pool).request();
  // 利用者の入力をクエリに直接挿入
  const result = await request.query(`SELECT * FROM users WHERE id = ${id}`);
  res.json(result);
});

変更後

javascript
const express = require('express');
const mssql = require('mssql');

app.get('/user', async (req, res) => {
  const id = parseInt(req.query.id, 10); // 整数変換の例: 入力は別途検証する
  const pool = mssql.connect(/* config */);
  const request = (await pool).request();
  // 値をパラメーターとしてバインド
  request.input('userId', mssql.Int, id);
  const result = await request.query('SELECT * FROM users WHERE id = @userId');
  res.json(result);
});

変更前はidをクエリに直接挿入し、接続アカウントの権限内で意味を変えられる可能性があります。変更後は@userIdに値をバインドし、SQL構文と分離します。parseIntは厳密な入力検証ではありません。入力全体の形式、NaN、SQLのIntの範囲、リクエスト元のアクセス権限を確認してください。接続設定とエラー処理は省略しています。

参考資料