MySQLクエリでのSQLインジェクション

MySQLクエリでのSQLインジェクション

説明

利用者の入力をSQLクエリに直接挿入すると、攻撃者がクエリの構造を変更できる可能性があります。不正なSQLにより、認証の回避や、権限のないデータ閲覧・変更・削除につながるおそれがあります。

想定される影響

  • データベースの重要な情報を不正に閲覧、変更、削除される可能性があります。
  • ログインや権限確認のクエリを操作され、アクセス制御を回避されるおそれがあります。
  • 破壊的なクエリやデータベース構造の変更で、サービスが停止する可能性があります。

対処方法

  • クエリの値にはパラメーターバインドやprepared statementを使ってください。
  • 利用者の入力をSQL文字列に直接挿入しないでください。
  • ORMでも値をバインドするクエリメソッドを使ってください。
  • 入力の形式、長さ、許可する値を検証し、想定外の入力を拒否してください。

例

connは事前に用意したMySQL2のPromise接続またはプールです。接続の作成、エラー処理、リクエストごとのアクセス権限の確認は省略しています。

変更前

javascript
const mysql = require('mysql2');
async function getUserById(req, res) {
  let userId = req.query.userId;
  let sql = "SELECT * FROM users WHERE id = " + userId; // 入力をSQLに直接挿入
  const [rows, fields] = await conn.execute(sql);
  res.json(rows);
}

変更後

javascript
const mysql = require('mysql2');
async function getUserById(req, res) {
  let userId = req.query.userId;
  // 値をパラメーターとしてバインド
  const [rows, fields] = await conn.execute(
    "SELECT * FROM users WHERE id = ?", [userId]
  );
  res.json(rows);
}

変更前はuserIdをSQLに連結するため、クエリを操作される可能性があります。変更後はexecuteに固定のSQLと値の配列を別々に渡し、prepared statementの?に値をバインドします。値はSQL構文と区別して処理されます。入力の検証と、その利用者のデータにアクセスする権限は別途確認してください。

参考資料