必须用SqlParameter替代字符串拼接,这是唯一能堵住所有SQL注入路径的实操方式;需显式指定参数类型与长度,禁用AddWithValue,且存储过程、EF Core原生SQL等所有执行场景均须严格参数化。

直接用 SqlParameter 替代字符串拼接,不是“加个过滤”或“转义单引号”就能过关——.NET Core 完全支持且强制推荐参数化查询,这是唯一能堵住所有注入路径的实操方式。
查 ExecuteReader 和 ExecuteScalar 调用点
漏洞几乎都藏在手动构造 SQL 字符串后调用执行方法的地方。重点扫以下位置:
-
.cs文件中所有含ExecuteReader()、ExecuteScalar()、ExecuteNonQuery()的代码段 -
DbContext.Database.ExecuteSqlRaw()或FromSqlRaw()调用(尤其带$""插值的) - DTO 或 Controller 层直接把
Request.Query["id"]、body.name拼进 SQL 的逻辑 - 日志里出现过
SqlException且错误信息含unclosed quotation mark或incorrect syntax near的堆栈
用 SqlParameter 时必须显式指定类型和长度
不写类型或乱用 SqlDbType.Variant 会导致隐式转换,可能绕过索引、截断输入、甚至触发类型推断失败。关键约束:
- 字符串参数必须用
SqlDbType.NVarChar+ 显式长度,例如new SqlParameter("@name", SqlDbType.NVarChar, 50) { Value = name } - 数字型(
int、long、Guid)必须走对应类型,不能塞进SqlDbType.VarChar里 -
null值要显式赋DBNull.Value,别让Value = null自动转成空字符串 - 避免用
cmd.Parameters.AddWithValue()—— 类型推断不可控,比如把"123"推成VarChar而非Int
Entity Framework Core 中避开 FromSqlRaw 的坑
EF Core 允许原生 SQL,但 FromSqlRaw 和 ExecuteSqlRaw 默认不校验参数,极易误用:
- ❌ 错误写法:
context.Users.FromSqlRaw($"SELECT * FROM users WHERE name = '{name}'") - ✅ 正确写法:
context.Users.FromSqlRaw("SELECT * FROM users WHERE name = @name", new SqlParameter("@name", name)) - 若需动态列名或表名(极少见),绝不能参数化,必须白名单硬编码校验,例如
if (!new[] { "created_at", "updated_at" }.Contains(sortField)) throw ... - 用
AsEnumerable()后再Where()过滤,不如直接用 LINQ 查询 —— EF Core 会生成安全参数化 SQL
存储过程调用也得走参数化
很多人以为“进了存储过程就安全了”,但若在 SP 内部用 EXEC(@sql) 或 sp_executesql 拼接用户输入,漏洞只是从 C# 移到了 SQL Server:
- 检查所有
CREATE PROCEDURE里是否含EXEC(、sp_executesql、CONCAT(或+拼接变量 - 传入 SP 的参数仍需用
SqlParameter,不能cmd.CommandText = "exec sp_get_user @id = " + id - SP 内部如需动态条件,优先用
CASE WHEN或多个IF分支,而非拼字符串执行
最易被忽略的是数字型参数——id=1 OR 1=1 能绕过所有字符串过滤,却逃不过 SqlParameter("@id", SqlDbType.Int) 的类型强约束。别信“这个字段只输数字,不用防”,攻击者永远比你想象的更懂怎么让 int.Parse 失效。

















