EF Core 默认防SQL注入,因Where等操作使用参数化查询;但动态字段名、表名、ORDER BY等SQL结构部分无法参数化,须用白名单校验,FromSqlRaw等需手动参数化,否则仍存在注入风险。

EF Core 默认就防 SQL 注入,但前提是别亲手把它关掉——绝大多数漏洞不是 EF 不行,而是你绕过了它的安全机制。
Where 查询为什么默认安全?
EF Core 的 IQueryable 表达式树(比如 _context.Users.Where(u => u.Email == email))在生成 SQL 时,会把 email 作为独立参数传给数据库,不会拼进 SQL 字符串里。数据库收到的是类似 WHERE [Email] = @p0 这样的语句,@p0 是绑定的值,不是可执行代码。
- 只要不调用
.ToString()、Expression.ToString()或手动转成字符串再执行,就保持安全 - 字段名(如
Email)必须是硬编码或来自白名单,不能是用户输入的字符串再反射取属性——u.GetType().GetProperty(fieldName)是高危操作 - 避免在
Where里拼接表达式树:例如Expression.AndAlso(left, right)后编译成Func<>,会导致内存过滤而非数据库下推,且字段名若动态拼接仍可能引入列名注入
FromSqlRaw 和 ExecuteSqlRaw 怎么写才不破防?
FromSqlRaw 和 ExecuteSqlRaw 本身不做任何参数化,传进去的字符串原样交给数据库驱动。你写 FromSqlRaw($"SELECT * FROM Users WHERE Name = '{name}'"),恶意输入早在 C# 层就混进 SQL 了。
- 必须用位置占位符:
.FromSqlRaw("WHERE Status = {0} AND CreatedAt > {1}", "Active", DateTime.UtcNow.AddDays(-7)) - 或命名参数 +
SqlParameter:.FromSqlRaw("WHERE Name = @name", new SqlParameter("@name", name)) - EF Core 5+ 支持
AddParameter链式调用,但仅限FromSqlRaw返回的IQueryable后续使用,ExecuteSqlRaw不支持 -
FromSqlInterpolated看似自动参数化,但它只处理纯变量插值(如{name}),不处理{name + "%"}或字符串拼接结果——C# 层拼完再传进去,等于没防
表名、列名、ORDER BY 这些怎么处理?
参数化只对数据值有效,对 SQL 结构部分(表名、字段名、排序方向、GROUP BY 字段等)完全无效。数据库引擎根本不支持参数化标识符。
- 动态表名/列名必须走白名单校验:
new[] { "Name", "Email", "CreatedAt" }.Contains(sortField),否则直接拒绝 -
ORDER BY {0}这种写法是错的、危险的,EF 不会帮你转义,数据库也不认这种参数 - 不要试图用
SqlParameter包裹字段名——它会被当作文本值,导致ORDER BY 'Name',排序失效 - 需要高度动态结构时,优先考虑重构为多个预定义查询,或用查询生成器(如
LINQKit)组合表达式树,而不是拼 SQL
最常被忽略的点是:字段名和排序方向这类结构控制,根本不在参数化的保护范围内;很多人以为用了 {0} 就万事大吉,结果把用户输入的 sort=Email; DROP TABLE Users-- 直接拼进了 SQL 结构里——防住了值,却放过了语法。

















