SqlParameter 可彻底防止 SQL 注入,需用 @param 占位并显式绑定值,EF Core 中避免 FromSqlRaw/FromSqlInterpolated 拼接,输入过滤无效,必须结合最小权限与敏感信息保护。

直接用 SqlParameter 替换字符串拼接
绝大多数 ASP.NET Core 项目里的 SQL 注入漏洞,根源就是把用户输入直接拼进 SqlCommand 的 SQL 字符串里。比如写成 "SELECT * FROM Users WHERE Name = '" + name + "'" —— 这种写法只要 name 是用户可控的,就等于给攻击者开了后门。
正确做法是彻底剥离 SQL 结构和数据:用 SqlParameter 显式绑定值,让数据库引擎自己处理类型和转义。
-
SqlCommand构造时只写固定 SQL 模板,所有变量位置用@paramName占位 - 调用
command.Parameters.Add(new SqlParameter("@name", name))绑定值,不要用string.Format或$""插入 - 即使参数是数字或布尔值,也必须走
SqlParameter,不能靠“它只是个数字”放松要求 - 注意
SqlParameter的SqlDbType类型要与数据库字段匹配(比如nvarchar对应SqlDbType.NVarChar),否则可能触发隐式转换绕过校验
Entity Framework Core 查询也得防着点
很多人以为用了 EF Core 就自动免疫 SQL 注入,其实不然。EF Core 安全的前提是——你没手动拼 SQL。
以下写法依然危险:
context.Users.FromSqlRaw("SELECT * FROM Users WHERE Name = '" + name + "'")-
context.Users.FromSqlInterpolated($"SELECT * FROM Users WHERE Name = {name}")(注意是FromSqlInterpolated,不是Where链式调用) - 在
raw SQL中用$"{value}"插入,哪怕 value 是ToString()过的
安全写法只有两种:
- 纯 LINQ 查询:
context.Users.Where(u => u.Name == name)(EF 自动参数化) - 必须用 raw SQL 时,只用
FromSqlRaw+SqlParameter,例如:context.Users.FromSqlRaw("SELECT * FROM Users WHERE Name = @name", new SqlParameter("@name", name))
别信“我做了输入过滤”这种说法
正则过滤 union、select、; 等关键字,或者对单引号做简单替换(如改成两个单引号),在现代攻击手法面前基本无效。攻击者会用编码、大小写混淆、注释符绕过,甚至利用数据库特性(如 MySQL 的 /**/)拆分关键词。
真正有效的输入处理只有一条:确认数据类型 + 限制长度 + 白名单校验(比如状态字段只接受枚举值),但这只是辅助手段,不能替代参数化查询。
-
int.TryParse(input, out int id)后再传给查询,比任何字符串过滤都可靠 - 对用户名、邮箱等文本字段,用
StringLengthAttribute在模型层限制长度,并在数据库字段定义中同步设MAX_LENGTH - 永远不要在 SQL 层做业务逻辑判断(比如用
WHERE Role IN (@roles)传入逗号分隔字符串),这种场景该用循环或表值参数(DataTable+SqlDbType.Structured)
检查连接字符串和数据库权限配置
参数化能防注入,但挡不住攻击者拿到数据库连接后干坏事。如果应用账号有 db_owner 权限,哪怕没有注入漏洞,一旦其他环节失守(比如反序列化漏洞),整个库就裸奔了。
- 生产环境的数据库账号必须遵循最小权限原则:只给
SELECT/INSERT/UPDATE/DELETE所需的表,禁用EXECUTE、CREATE、DROP - 连接字符串里不要硬编码密码;用 Azure Key Vault、Secret Manager 或托管标识代替
- 避免在错误页面暴露完整 SQL 语句或数据库结构(比如开启
DeveloperExceptionPage仅限开发环境) - EF Core 的
EnableSensitiveDataLogging()必须关掉,否则日志里会打印带参数值的 SQL
参数化是底线,不是可选项;权限控制和错误处理是兜底。漏掉任意一环,都可能让防御形同虚设。

















