FlawOpen

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

Is Entity Framework Core FromSqlRaw safe?

Reference page
Short answer

Yes, if values stay separate from the SQL text. FromSqlRaw accepts raw SQL, so concatenating or interpolating untrusted input into its string can change the query. Use its separate parameter arguments, or pass an interpolated FormattableString to FromSqlInterpolated (or EF Core 7+ FromSql). Think of a parameter as a sealed value slot: the database receives the value, not new SQL instructions.

UNSAFE — interpolation becomes a plain string
// The interpolation is evaluated into a string first.
var sql = $"SELECT * FROM Users WHERE Id = {id}";

// FromSqlRaw receives SQL text, not a FormattableString.
var query = context.Users.FromSqlRaw(sql);
SAFE — values remain parameters
// EF Core 3–6: positional placeholder plus separate value.
var byRawParameter = context.Users.FromSqlRaw(
    "SELECT * FROM Users WHERE Id = {0}", id);

// EF Core 3+: pass interpolation to the parameterizing API.
var byInterpolation = context.Users.FromSqlInterpolated(
    $"SELECT * FROM Users WHERE Id = {id}");

// EF Core 7+: FromSql also accepts FormattableString.
var byFromSql = context.Users.FromSql(
    $"SELECT * FROM Users WHERE Id = {id}");

The counterintuitive part

FromSqlInterpolated accepts a FormattableString. EF Core reads its format and arguments and sends each interpolated value as a separate database parameter; no compile-time interception is involved. EF Core 7 added FromSql with the same safe interpolated-string behavior. Earlier versions use FromSqlInterpolated. FromSqlRaw is also safe when placeholders such as {0} match values passed as separate arguments. An interpolated string first stored in a string has already lost its parameter metadata.

Parameters bind values, not column names

A database parameter can represent a value, not a table name, column name, SQL operator, or sort direction. If a query must vary its structure, choose each identifier from a fixed allow-list in application code; do not try to parameterize identifiers, and do not treat character filtering as a substitute for an allow-list. Also review authorization and query cost: a valid column can still expose sensitive fields or trigger expensive scans.

How to check your codebase

rg -n -g '*.cs' '(FromSqlRaw|FromSqlInterpolated|FromSql)\s*\(' .
For each call, check the overload and argument type. Trace untrusted data for concatenation, interpolation passed to FromSqlRaw, or a FormattableString converted to string; inspect dynamic identifiers separately.

FAQ

Is FromSqlRaw safe if it receives parameters?

Yes, when the SQL contains placeholders and values are passed as separate arguments, for example FromSqlRaw("... {0}", value). Do not concatenate or interpolate the value into the SQL string first.

Why is FromSqlInterpolated safe with a C# interpolated string?

It accepts FormattableString, which preserves the format and values separately. EF Core turns each value into a database parameter. If you assign the interpolation to a string first or pass it to FromSqlRaw, that protection is lost.

Can parameters replace table or column names?

No. Parameters represent values only. For dynamic identifiers, map user choices to a fixed allow-list of known identifiers and review authorization and query cost.

References

See also SQL Injection (CWE-89): How It Works and How to Prevent It in Every Language — flawopen.com FastAPI SQL Injection: What Pydantic Does and Doesn’t Stop GORM Raw SQL: Prevent SQL Injection in Go Is Escaping Quotes Enough to Prevent SQL Injection? Is Using an ORM Enough to Prevent SQL Injection? Is Knex.js Safe from SQL Injection? MyBatis ${} vs #{}: Prevent SQL Injection