flawopen.com/SQL Injection in C#/EF Core FromSqlRaw
Is Entity Framework Core FromSqlRaw safe?
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.
// 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);
// 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.
- ✓For values, use FromSqlInterpolated/FromSql with an inline interpolated string, or FromSqlRaw with separate parameters.
- ✓Never pass concatenated or interpolated user data to FromSqlRaw. Do not first convert a FormattableString to string.
- ✓For dynamic identifiers, select from a fixed allow-list. Separately check whether the selected field is authorized and efficiently indexed.
- ✓Prefer LINQ when it expresses the query clearly; parameterization prevents SQL syntax injection but does not enforce authorization or business rules.
How to check your codebase
rg -n -g '*.cs' '(FromSqlRaw|FromSqlInterpolated|FromSql)\s*\(' .FAQ
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.
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.
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
- Microsoft Learn — EF Core SQL queries and parameterization
- Microsoft Learn — FromSqlRaw API
- Microsoft Learn — FromSqlInterpolated API
- OWASP — SQL Injection Prevention Cheat Sheet
- FlawOpen — SQL Injection in C#