flawopen.com/SQL Injection/ORM で十分?
ORM を使えば SQL インジェクションを防げるか
ORM は注文票を見て料理する厨房のようなものです。通常のクエリビルダーは値を分けて扱います。生 SQL の近道は別の搬入口で、それぞれルールがあります。補間値を信頼する前に API を確認してください。
var sql = $"SELECT * FROM dbo.Blogs WHERE Url = '{url}'";
var rows = await context.Blogs
.FromSqlRaw(sql)
.ToListAsync();
// User text is inserted into SQL before execution.
var rows = await context.Blogs
.FromSql($"SELECT * FROM dbo.Blogs WHERE Url = {url}")
.ToListAsync();
// EF Core 7+: interpolation becomes a SQL parameter.
各 API における値の扱い
ORM のクエリビルダーと生 SQL API は同じセキュリティ境界ではありません。公式ドキュメントは、Django raw(sql, params)、実行パラメーターを分ける SQLAlchemy text()、Rails のプレースホルダー条件、EF Core 7 以降の FromSql / FromSqlInterpolated、GORM Raw(sql, args...)、Prisma のタグ付き $queryRaw で値を安全にバインドする方法を説明しています。API ごとに挙動は異なります。FromSqlRaw は値を別に渡せば安全ですが、ユーザー入力を SQL 文字列に挿入すると危険です。$queryRawUnsafe も値を別にバインドできますが、信頼できない文字列の連結は危険です。メソッド名だけでは値の扱いは分かりません。各 API の仕様に沿って値をバインドしてください。SQL 識別子やクエリ構造はパラメーター化できないため、アプリ管理の固定許可リストから選びます。詳細は各ガイドを参照してください。
「十分」が実際に要求すること
- ✓通常の値やフィルターには ORM のクエリビルダーを使う。
- ✓生 SQL では API の仕様に沿ってすべての値をバインドする。
- ✓動的識別子はアプリ管理の固定許可リストから選ぶ。SQL 構造はパラメーター化できない。
- ✓生クエリで文字列連結、補間、危険なメソッドを確認する。
FAQ
いいえ。値を別に渡すとバインドする生 SQL API も、補間値をパラメーターとして受け取る安全な API もあります。EF Core 7 以降の FromSql は FormattableString を受け取り、FromSqlRaw では値を別に渡します。Prisma の $queryRawUnsafe も値をバインドできますが、自分で作った SQL はサニタイズしません。メソッドの契約を確認し、信頼できない値を SQL 構造に含めないでください。