FlawOpen

flawopen.com/C#のSQL Injection/EF Core FromSqlRaw

Entity Framework CoreのFromSqlRawは安全ですか?

リファレンスガイド
簡潔な回答

値がSQLテキストと分離されていれば安全です。FromSqlRawは生SQLを受け取るため、信頼できない入力を連結または補間するとクエリが変わるおそれがあります。値は個別のパラメーター引数で渡すか、補間で生成したFormattableStringをFromSqlInterpolated(EF Core 7以降ではFromSqlも可)に渡します。パラメーターは封印された値の枠のようなものです。データベースに届くのは値であり、新しいSQL命令ではありません。

危険 — 補間結果が通常の文字列になる
// 補間は先に文字列として評価されます。
var sql = $"SELECT * FROM Users WHERE Id = {id}";

// FromSqlRawが受け取るのはSQLテキストで、FormattableStringではありません。
var query = context.Users.FromSqlRaw(sql);
安全 — 値はパラメーターのまま
// EF Core 3–6:位置プレースホルダーと別引数の値。
var byRawParameter = context.Users.FromSqlRaw(
    "SELECT * FROM Users WHERE Id = {0}", id);

// EF Core 3以降:補間文字列をパラメーターAPIに渡します。
var byInterpolation = context.Users.FromSqlInterpolated(
    $"SELECT * FROM Users WHERE Id = {id}");

// EF Core 7以降:FromSqlもFormattableStringを受け取ります。
var byFromSql = context.Users.FromSql(
    $"SELECT * FROM Users WHERE Id = {id}");

見落としやすいポイント

FromSqlInterpolatedはFormattableStringを受け取ります。EF Coreは書式と引数を読み取り、補間された各値を個別のデータベースパラメーターとして送ります。コンパイル時に補間を横取りするわけではありません。EF Core 7では同じ安全な動作をするFromSqlが追加されました。それ以前のバージョンではFromSqlInterpolatedを使います。FromSqlRawも、{0}のようなプレースホルダーに対応する値を別引数で渡せば安全です。補間文字列を先にstringへ代入すると、パラメーターのメタデータは失われます。

パラメーターが束縛するのは値であり、列名ではありません

データベースパラメーターで指定できるのは値であり、テーブル名、列名、SQL演算子、並び順ではありません。クエリ構造を切り替える場合は、アプリケーションコード内の固定許可リストから識別子を選びます。識別子をパラメーター化したり、文字フィルターを許可リストの代わりにしたりしないでください。認可とクエリコストも確認します。有効な列でも機密情報を公開したり、高コストなスキャンを起こしたりする可能性があります。

コードベースの確認方法

rg -n -g '*.cs' '(FromSqlRaw|FromSqlInterpolated|FromSql)\s*\(' .
各呼び出しでオーバーロードと引数の型を確認します。信頼できないデータの連結、FromSqlRawへの補間結果の受け渡し、FormattableStringからstringへの変換を追跡します。動的識別子は別途確認します。

よくある質問

FromSqlRawはパラメーターを受け取れば安全ですか?

SQLにプレースホルダーがあり、値を別引数として渡す場合は安全です。例:FromSqlRaw("... {0}", value)。値を先にSQL文字列へ連結・補間しないでください。

C#の補間文字列を使ってもFromSqlInterpolatedが安全なのはなぜですか?

FormattableStringを受け取り、書式と値を分けて保持するためです。EF Coreは各値をデータベースパラメーターに変換します。先にstringへ代入したりFromSqlRawに渡したりすると保護が失われます。

パラメーターでテーブル名や列名を置き換えられますか?

いいえ。パラメーターは値だけを表します。動的識別子はユーザーの選択を既知の識別子の固定許可リストに対応付け、認可とクエリコストを確認してください。

参考資料

関連項目 SQL インジェクション(CWE-89)の仕組みと各言語での防ぎ方 — flawopen.com FastAPIのSQLインジェクション:Pydanticが防ぐこと・防げないこと GORM Raw SQL:GoでSQLインジェクションを防ぐ 引用符のエスケープだけでSQL Injectionを防げる? ORM を使えば SQL インジェクションを防げるか Knex.jsはSQL Injectionを防げますか? MyBatisの${}と#{}:SQL Injectionを防ぐ