根本不用在Action参数上做手脚——危险的是SQL拼接而非参数本身;必须用SqlParameter在SqlCommand执行前参数化,禁用字符串拼接;存储过程+显式类型参数更安全;EF Core中FromSqlRaw拼接变量即破防。

根本不用在 Action 参数上做手脚——string name 这类参数本身不危险,危险的是你把它直接拼进 SQL 字符串里。只要后续没用 SqlParameter,光靠 ActionFilter 拦截、替换或删掉单引号,全是掩耳盗铃。
为什么 ActionFilter 清洗参数根本挡不住注入
很多项目加了类似 [SqlInjectFilter] 的特性,对 name 做 Replace(";", "").Replace("--", "") 等操作。这看似主动,实则失效:
- 攻击者可用嵌套注释
/* */、Unicode 编码(如')、空格绕过正则匹配 - 清洗发生在 MVC 绑定后,但 SQL 拼接通常在 DAL 层,Filter 根本不知道你后面怎么用这个值
- 清洗破坏原始语义:用户真名叫 “O’Reilly” 就查不到,而攻击者早用
1' OR '1'='1绕过了清洗逻辑
SqlParameter 必须出现在 SqlCommand 执行前那一层
参数化不是“加个 @ 符号”就完事,关键动作必须落在数据库命令构建环节:
-
cmd.CommandText里只能出现占位符,比如"WHERE Name LIKE @name",绝不能出现"WHERE Name LIKE '%" + name + "%'" -
cmd.Parameters.Add("@name", SqlDbType.NVarChar).Value = name—— 类型要显式指定,避免隐式转换导致截断或性能抖动 - 如果用
AddWithValue,.NET 会自动推断类型,但对NULL或超长字符串可能误判为SqlDbType.Variant,引发索引失效
存储过程 + 参数化是更稳的组合
把查询逻辑下推到 SQL Server,比在 C# 里拼 SQL 更可控:
- 存储过程中所有输入都声明为
@name NVARCHAR(50),天然隔离数据与结构 - C# 端只调用
cmd.CommandType = CommandType.StoredProcedure,再用Parameters.Add绑定,链路更短、出错点更少 - DBA 可统一审计、限权:给应用账号只授予
EXECUTE权限,禁用SELECT直查表
别让 ORM 成为新漏洞入口
EF Core 用 Where(x => x.Name == name) 是安全的,但下面这些写法会破防:
-
context.Users.FromSqlRaw("SELECT * FROM Users WHERE Name LIKE '%" + name + "%'")—— 直接拼接,等于裸奔 -
context.Users.FromSqlInterpolated($"SELECT * FROM Users WHERE Name LIKE { $"%{name}%" }")—— 看似用了插值,但{ $"%{name}%" }仍是字符串拼接,没走参数化 - 正确写法是
context.Users.FromSqlRaw("SELECT * FROM Users WHERE Name LIKE @name", new SqlParameter("@name", $"%{name}%"))
最易被忽略的点:哪怕用了 EF Core,只要有一次手写 FromSqlRaw 并拼接变量,整条链路就塌了。安全不是“大部分时候对”,而是“每一次都对”。











