内插字符串本身不导致sql注入,但直接拼接用户输入会引发漏洞;$""与+拼接无本质区别,因编译器仅作字符串拼接,不识别sql语法或转义;参数化仅保护值,不保护表名、排序字段等结构部分。

内插字符串($"")本身不导致SQL注入,但一旦把用户输入直接拼进 $"SELECT * FROM Users WHERE name = '{txtName.Text}'" 这类语句里,就等于主动打开SQL注入大门。
为什么 $"" 拼 SQL 和 + 拼没区别?
因为编译器只是把 $"" 编译成普通字符串拼接——它不识别 SQL 语法,也不做任何转义或隔离。数据库收到的仍是完整字符串,而 SQL Server 只看内容,不管你是用 $""、string.Format 还是 StringBuilder.Append 生成的。
- 输入
txtName.Text = "admin' OR 1=1 --"→ 最终 SQL 变成WHERE name = 'admin' OR 1=1 --',条件恒真 - 前端加了
maxlength="20"或正则过滤单引号?攻击者可用CHAR(39)、/**/注释、Unicode 引号绕过 - 哪怕只在
WHERE里拼一个值,其他地方全用参数,只要这一处漏掉,整条语句就失效
FromSqlRaw 和 FromSqlInterpolated 的坑在哪?
EF Core 的 FromSqlRaw 完全不防注入;FromSqlInterpolated 只对 {变量} 做参数化,但对插值中的结构部分完全不处理。
-
context.Users.FromSqlRaw($"SELECT * FROM {tableName} WHERE id = {id}")→tableName和id都直拼,高危 -
context.Users.FromSqlInterpolated($"SELECT * FROM Users WHERE name = {name} ORDER BY {sortField}")→{name}安全,{sortField}是直接拼进 SQL 的,可被设为"id; DROP TABLE Logs--" - 正确写法:表名必须来自白名单,排序字段必须映射:
var sql = sortField switch { "name" => "ORDER BY user_name", _ => "ORDER BY id" };
为什么 AddWithValue 不算真正“参数化”?
它确实用了参数机制,但类型推断会破坏执行计划和数据完整性,属于“伪安全”。
- 传
"abc"→ 推断为VarChar(3),而数据库字段是VarChar(50)→ SQL Server 可能放弃索引,查询变慢十倍 - 传空字符串
""→ 推断为VarChar(0),某些驱动下会截断或报错 - 传
null→ 默认当DBNull.Value,但显式写DBNull.Value更可控,避免后期逻辑歧义 - 正确写法:
cmd.Parameters.Add(new SqlParameter("@name", SqlDbType.NVarChar) { Value = name ?? (object)DBNull.Value, Size = 100 });
最常被忽略的一点:参数化只保护值,不保护语句结构。生产环境里,ORDER BY、分页偏移、租户表路由这些动态部分,才是真实攻防焦点——它们没法加 @param,只能靠硬编码白名单或 QUOTENAME() 转义。











