FlawOpen

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 を使います。

参考資料

関連項目 SQL インジェクション(CWE-89)の仕組みと各言語での防ぎ方 — flawopen.com TypeORM QueryBuilder は SQL Injection に安全? Django ORMとSQL Injection:安全な書き方 Doctrine ORMのSQLインジェクション対策 EF Core FromSqlRaw:安全なSQLパラメーター化 FastAPIのSQLインジェクション:Pydanticが防ぐこと・防げないこと GORM Raw SQL:GoでSQLインジェクションを防ぐ