flawopen.com/SQL インジェクション/ストアドプロシージャ
ストアドプロシージャは SQL インジェクションを防ぎますか?
リファレンスページ
要点
いいえ。よくある誤解です。ストアドプロシージャが保護になるのは、内部でパラメーターをバインドする場合だけです。入力を連結してクエリ文字列を作り、EXEC や sp_executesql で実行すれば、アプリケーションでの文字列連結と同じリスクがあります。
危険 — 値を動的 SQL に連結
-- SQL Server T-SQL; dbo.Users.Id is NVARCHAR(50).
CREATE PROCEDURE dbo.GetUser
@UserId NVARCHAR(50)
AS
BEGIN
DECLARE @sql NVARCHAR(MAX) =
N'SELECT Id FROM dbo.Users WHERE Id = N'''
+ @UserId + N'''';
EXEC(@sql);
END;
安全 — sp_executesql でパラメーターをバインド
-- SQL Server T-SQL; same illustrative schema.
CREATE PROCEDURE dbo.GetUser
@UserId NVARCHAR(50)
AS
BEGIN
DECLARE @sql NVARCHAR(MAX) =
N'SELECT Id FROM dbo.Users WHERE Id = @id';
EXEC sys.sp_executesql
@stmt = @sql,
@params = N'@id NVARCHAR(50)',
@id = @UserId;
END;
この誤解が広まっている理由
「ストアドプロシージャを使う」は SQL インジェクションの万能な解決策としてよく挙げられます。通常のクエリで値をバインドパラメーターとして渡す場合は有効です。EXEC で動的 SQL を組み立て、信頼できない値を連結すれば、同じリスクが再発します。動的 SQL が必要なら sp_executesql で値をバインドします。テーブル名や列名は値としてバインドできないため、許可する選択肢を固定識別子へ対応付ける厳格な allowlist を使ってください。
よくある質問
sp_executesql は安全に使えますか?
はい。sp_executesql はパラメーターに対応します。宣言と値を分けて渡してください(例: EXEC sp_executesql @sql, N'@id INT', @id = @UserId)。値を @sql に連結してはいけません。動的なテーブル名や列名には、識別子をバインドできないため厳格な allowlist を使います。