動的 SQL を使用している場合は、パラメーター化されたクエリと SP_EXECUTESQL を使用する必要があります。
CREATE TABLE T (StartRow INT, StartEnd INT, CategoryID INT);
INSERT T VALUES (1, 10, 124), (1, 10, 125), (1, 10, 126);
DECLARE @StartRow INT = 1;
DECLARE @StartEnd INT = 10;
DECLARE @CategoryId NVARCHAR(50) ='124); DROP TABLE T; --';
DECLARE @SQL NVARCHAR(MAX);
SET @SQL = N'SELECT *
FROM T
WHERE StartRow = @Start
AND StartEnd = @End
AND CategoryID IN (' + @CategoryId + ')'
EXECUTE SP_EXECUTESQL @SQL, N'@Start INT, @End INT', @StartRow, @StartEnd;
動的 SQL の例
これは動的 SQL なしで SQL-Server の XML 拡張機能を使用して行うことができますが、必ずしもそうする必要があるかどうかはわかりませんが、いくつかのテストを実行してパフォーマンスを確認することをお勧めします。
DECLARE @StartRow INT = 1;
DECLARE @StartEnd INT = 10;
DECLARE @CategoryId NVARCHAR(50) ='124,125';
DECLARE @X XML = CAST(N'<root><catid>' + REPLACE(@CategoryID, ',', '</catid><catid>') + '</catid></root>' AS XML);
SELECT StartRow,
StartEnd,
CategoryID
FROM T
INNER JOIN
( SELECT [CatID] = cat.value('.', 'int')
FROM @X.nodes('/root/catid') c (cat)
) c
ON c.CatID = CategoryID;
XML 拡張を使用した例
クエリは基本的にコンマ区切りの文字列を XML に変換し、その xml を独自のテーブルに分割します。
これにより、不正な形式の文字列が実行されるのではなくエラーがスローされるため、SQL インジェクションのリスクも軽減されます。次の変数を検討してください。
DECLARE @StartRow INT = 1;
DECLARE @StartEnd INT = 10;
DECLARE @CategoryId NVARCHAR(50) ='124,125); DROP TABLE T; --';
XML拡張メソッドで実行すると、これが得られます
Msg 245, Level 16, State 1, Line 13
Conversion failed when converting the nvarchar value '124,125); DROP TABLE T; --' to data type int.
XML 拡張で SQL インジェクションが失敗する
動的 SQL を使用して実行すると、望ましい結果が得られますが、次回実行すると次のようになります。
Msg 208, Level 16, State 1, Line 1
Invalid object name 'T'.
SQL インジェクションの例
上記で使用した XML 方式以外にも文字列を分割する方法がありますが、動的 SQL が唯一の方法ではないことを示すためだけのものであり、入力文字列を制御できない場合は、それを使用しない方が賢明です。悪意のある文字列がデータを破損する可能性があるためです!
答えを完全にするために、テーブル値パラメーターを使用することもできます。例えば
CREATE TYPE dbo.IntegerList AS TABLE (value INT);
CREATE PROCEDURE dbo.ExampleProcedure @StartRow INT, @StartEnd INT, @CategoryID dbo.IntegerList READONLY
AS
BEGIN
SELECT *
FROM T
WHERE StartRow = @StartRow
AND StartEnd = @StartEnd
AND CategoryID IN (SELECT Value FROM @CategoryID)
END