私が使用するいくつかのストアドプロシージャでは、プロシージャの入力パラメータが指定されているかどうかに基づいてWHERE条件を補間する必要があります。潜在的な注入ポイントを回避するために、補間された基準の一部となる値にパラメーターバインディングを利用したいと思います。
準備済みステートメントに追加された基準、つまりバインドされるパラメーターの数はユーザー入力によって異なる場合があるため、EXECUTEステートメントに渡される変数を決定するために、以下の方法を考案しました。これは機能しますが、洗練されていないようです。
CREATE PROCEDURE foo (IN mandatory INT, IN optional INT, IN optional2 VARCHAR(20))
BEGIN
SELECT
0, '', '', mandatory, optional, optional2
INTO
@params, @sql, @where, @m, @o1, @o2;
IF (@o1 > '' AND @o1 IS NOT NULL) THEN
SET @where = CONCAT(@where, ' AND field = ?');
SET @params = @params + 1;
END IF;
IF (@o2 > '' AND @o2 IS NOT NULL) THEN
SET @where = CONCAT(@where, ' AND field2 = ?');
SET @params = @params + 3;
END IF;
SET @sql = CONCAT('
SELECT id, bar FROM table
WHERE
baz = ?
', @where
);
PREPARE STMT FROM @sql;
CASE @params
WHEN 0 THEN EXECUTE STMT USING @m;
WHEN 1 THEN EXECUTE STMT USING @m, @o1;
WHEN 3 THEN EXECUTE STMT USING @m, @o2;
WHEN 4 THEN EXECUTE STMT USING @m, @o1, @o2;
END CASE;
DEALLOCATE PREPARE STMT;
END$$
私は代替案を知っています:
しかし、SQLだけでEXECUTEステートメントの動的構築を処理したいという思いが他の誰かに出くわしたのではないかと思っていました。
少なくともこの例では、これよりも簡単な方法があります。オプティマイザは常に、有効な結果セットを取得するために必要な作業量が最も少ないパスが選択されたパスになるように、クエリの実行を計画しようとしていることに注意してください。
_SELECT * FROM t1 WHERE (col1 = 'foo') OR (1 = 1);
_
サーバーはalwaysすべての行を返します。1= 1には「はい、この行はWHERE
句に一致する」と結論付けるために必要なすべての真実性が含まれているためです...オプティマイザがより明確でシンプルな「低コスト」アプローチを見つけたため、col1の値をスキャンして「foo」が含まれているかどうかを確認する必要はありません... _(any expression) OR (TRUE)
_は常にtrue ...と真実への最短パスオプティマイザが検索して取得するはずのものです。 _1 = 1
_が行ごとかどうかを評価する必要はありません。これは定数式だからです。
準備されたステートメントは必要ありません。プロシージャのクエリだけです。
_SELECT id, bar
FROM table
WHERE baz = mandatory
AND (optional IS NULL OR field = optional)
AND (optional2 IS NULL OR field2 = optional2);
_
プログラム変数の「オプション」がnullの場合、オプティマイザは、実行中のクエリのコンテキストでは「オプション」が定数であるため、式全体(その行の括弧内)が各行に対してtrueであることを認識します。値、および_optional is NULL
_が常にtrueであるように、_1 = 1
_は常にtrueになります。オプティマイザは、これも定数式であることを認識しています。 「フィールド」の内容を評価する必要はありません。これは、OR
式が事前にtrueであると判断されているためです。同じことがoptional2にも言えます。
反対に、「optional」がnullでない場合、その式を真にすることは不可能であるため、_optional IS NULL
_は最適化され、AND (field = optional)
が残ります。同様に、「optional2」についても同様です。
オプティマイザには、いずれの場合も、必要に応じてインデックスを使用できるクエリが残ります。
クエリは準備されたステートメントではなくプロシージャ本体にあるため、サーバーは変数内のデータとリテラルクエリの違いを曖昧にすることができないため、SQLインジェクションは不可能です。