FlawOpen

flawopen.com/SQL Injection/ORM で十分?

ORM を使えば SQL インジェクションを防げるか

リファレンスページ
手短に言うと

ORM は注文票を見て料理する厨房のようなものです。通常のクエリビルダーは値を分けて扱います。生 SQL の近道は別の搬入口で、それぞれルールがあります。補間値を信頼する前に API を確認してください。

危険 — FromSqlRaw SQL にユーザー入力
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.
安全 — FromSql で補間値をバインド(EF Core 7+)
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 識別子やクエリ構造はパラメーター化できないため、アプリ管理の固定許可リストから選びます。詳細は各ガイドを参照してください。

「十分」が実際に要求すること

FAQ

生 SQL メソッドは常に危険ですか?

いいえ。値を別に渡すとバインドする生 SQL API も、補間値をパラメーターとして受け取る安全な API もあります。EF Core 7 以降の FromSql は FormattableString を受け取り、FromSqlRaw では値を別に渡します。Prisma の $queryRawUnsafe も値をバインドできますが、自分で作った SQL はサニタイズしません。メソッドの契約を確認し、信頼できない値を SQL 構造に含めないでください。

参考文献

関連項目 SQL インジェクション(CWE-89)の仕組みと各言語での防ぎ方 — flawopen.com Knex.jsはSQL Injectionを防げますか? MyBatisの${}と#{}:SQL Injectionを防ぐ MySQLiとPDO:安全なPrepared Statement NHibernate は SQL Injection を防げますか? MongoDB NoSQL Injection:安全なクエリフィルター 数値 ID でも文字列連結で SQL Injection が起きますか?