ef core 的 linq 查询(如 where、find)默认参数化,防 sql 注入;fromsqlraw/executesqlraw 若字符串插值则危险,须用参数化或白名单校验;动态查询应构建表达式树而非拼接 sql。

直接用 EF Core 做查询,只要不手拼 SQL 字符串,就基本不会出 SQL 注入问题。但很多人误以为“用了 EF 就安全了”,结果在 FromSqlRaw、ExecuteSqlRaw 或字符串插值里翻车。
为什么 Where 和 Find 是安全的
EF Core 的 LINQ 查询(比如 context.Users.Where(u => u.Name == name))默认走参数化执行:SQL 语句模板和参数值分离,数据库引擎只把变量当数据处理,不解析为语法结构。
- 底层生成类似
SELECT * FROM Users WHERE Name = @p0,@p0是绑定的参数,不是字符串拼接 - 即使
name是"admin' OR '1'='1",也会被当作完整字符串值传入,不会破坏 SQL 结构 - 所有
Find、Single、First及带 lambda 的Where都自动参数化,无需额外操作
FromSqlRaw 和 ExecuteSqlRaw 是高危区
这两个方法明确告诉你“我要写原生 SQL”,一旦用 $"..." 插值或 string.Format 拼接用户输入,立刻回归手写 SQL 的风险等级。
- ❌ 危险写法:
context.Users.FromSqlRaw($"SELECT * FROM Users WHERE Name = '{name}'") - ✅ 正确写法:
context.Users.FromSqlRaw("SELECT * FROM Users WHERE Name = {0}", name)(位置参数) - ✅ 更推荐:
context.Users.FromSqlRaw("SELECT * FROM Users WHERE Name = @name", new SqlParameter("@name", name)) - 注意:
FromSqlInterpolated看似用$,但它会自动参数化插值内容,是安全的 —— 但仅限于直接插变量,不能插表达式或拼接逻辑
动态查询构建别绕过 EF 的参数化机制
常见需求如“按多个字段模糊搜索”,容易写出 if 堆叠 + 字符串拼接的 SQL。这不是 EF 的错,是你主动放弃了它的保护。
- ❌ 错误思路:用
StringBuilder拼WHERE条件,最后喂给FromSqlRaw - ✅ 推荐做法:用 LINQ 动态构建表达式树,例如用
ExpressionVisitor或第三方库如Linq.Dynamic.Core,保持整个链路在 EF 参数化体系内 - 如果必须用原生 SQL,所有用户可控字段(如搜索关键词、排序字段名)必须严格白名单校验,不能仅靠参数化 —— 参数化防不住列名、表名、
ORDER BY子句注入
连接字符串和上下文配置本身也要防注入
很少有人想到,ConnectionString 如果来自配置中心或环境变量,且未做校验,也可能被污染 —— 特别是启用了 Connect Timeout、Application Name 这类可被攻击者控制的参数时。
- EF Core 的
UseSqlServer不会对连接字符串内容做注入检查,它原样交给SqlClient - 确保连接字符串来源可信;若需动态构造,只拼接已知安全的部分(如服务器地址、数据库名),禁用用户可触达的参数项
- 另外,
EnableRetryOnFailure等策略配置不影响注入防护,但错误地启用它可能掩盖真实异常,反而让注入点更难被发现
最易被忽略的一点:ORM 安全性完全取决于你调用它的姿势。EF Core 不会阻止你写 FromSqlRaw($"...{userInput}..."),就像刀不会阻止你割自己手指 —— 它只提供安全的握柄方式,而你得真握住它。











