EF Core 8 默认防SQL注入仅限LINQ查询(如Where),FromSqlRaw/ExecuteSqlRaw需手动参数化,动态表名列名等SQL结构部分必须用白名单校验。

EF Core 8 默认不防 SQL 注入——除非你用对了方法。 它只对 LINQ 查询(如 Where、OrderBy)自动参数化;一旦调用 FromSqlRaw、ExecuteSqlRaw 或拼接 SQL 字符串,安全就完全交到你手上。
FromSqlRaw 和 FromSqlInterpolated 怎么选?
二者都用于查询(SELECT),返回实体集合,但参数处理逻辑不同:
-
FromSqlRaw:字符串原样传入,必须手动参数化。支持位置占位符({0})或SqlParameter对象,例如:context.Users.FromSqlRaw("SELECT * FROM Users WHERE Email = @email", new SqlParameter("@email", inputEmail)) -
FromSqlInterpolated:只接受一个插值字符串($"..."),自动把变量转为命名参数(如@p0)。但它不处理表达式,$"WHERE Name LIKE {name + "%"}"是危险的——C# 层已拼好字符串,EF 不再干预 - 模糊查询(
LIKE)建议用FromSqlRaw+SqlParameter,避免FromSqlInterpolated对%等符号做意外转义
ExecuteSqlRaw / ExecuteSqlRawAsync 必须这样传参
增删改、存储过程调用等非查询操作,只能用 ExecuteSqlRaw(同步)或 ExecuteSqlRawAsync(异步),它们完全不解析 SQL 字符串,全靠你传参安全:
- 错误写法:
context.Database.ExecuteSqlRaw($"DELETE FROM Users WHERE Id = {id}")—— 用户输入直接进 SQL - 正确写法(位置占位符):
context.Database.ExecuteSqlRaw("UPDATE Users SET Status = {0} WHERE Id = {1}", "Active", userId) - 更推荐命名参数(尤其多参数时):
context.Database.ExecuteSqlRaw("DELETE FROM Users WHERE Email = @email", new SqlParameter("@email", email)) - 注意:
ExecuteSqlRaw不支持链式AddParameter,参数必须一次性传完
表名、字段名、ORDER BY 这些不能参数化,怎么办?
SQL 结构部分(如 FROM 表名、ORDER BY 列名、GROUP BY)无法用参数占位,用户输入若直接拼入,就是列名注入:
- 禁止:
var sql = $"SELECT * FROM {tableName} ORDER BY {sortColumn}"; - 必须用白名单校验:
if (!new[] { "Users", "Posts", "Comments" }.Contains(tableName)) throw new ArgumentException(); - 排序字段同理:
if (!new[] { "Id", "CreatedAt", "Name" }.Contains(sortColumn)) throw new ArgumentException(); - 反射取属性(
entity.GetType().GetProperty(fieldName))也属高危,字段名必须硬编码或来自可信源
最易被忽略的一点:哪怕用了 FromSqlInterpolated,只要你在插值前做了字符串拼接(比如 $"SELECT * FROM {table} WHERE ..."),EF 就彻底失效——它只保护 $"" 内部的纯变量,不保护变量之外的任意动态片段。

















