模型绑定本身不防sql注入,它仅负责将请求数据解析为对象,防注入关键在于后续sql构建是否参数化;直接字符串拼接(如orderby字段、表名、in子句)必须白名单校验或改用iqueryable下推。

模型绑定本身不防SQL注入,它只是把请求数据转成对象,防注入的关键在后续怎么用这些数据——尤其是怎么拼SQL。
模型绑定后直接拼接字符串就是高危操作
很多人误以为只要用了 [FromBody] 或 [FromQuery] 就安全了,其实绑定完的 username、sortField 还是普通字符串。一旦你把它塞进 SQL 里拼接,比如 $"SELECT * FROM Users ORDER BY {input.SortBy}",攻击者传 name; DROP TABLE Users-- 就直接执行。
- 模型绑定只负责“解析”,不负责“消毒”或“参数化”
-
string类型的绑定结果和Request.Query["q"]拿到的值本质一样,都是原始用户输入 - 哪怕用了
[Range]、[RegularExpression]等验证特性,也只影响绑定是否成功,不影响后续 SQL 构建逻辑
EF Core 的 IQueryable 查询才是真正的防护层
真正起作用的是你用绑定后的值去构建 IQueryable 表达式,而不是拼 SQL 字符串。EF Core 会把 where 条件里的值自动转成参数,不会出现在生成的 SQL 文本中。
- ✅ 安全写法:
_context.Users.Where(u => u.Email == model.Email)——model.Email被当参数传给数据库 - ❌ 危险写法:
_context.Users.FromSqlRaw($"SELECT * FROM Users WHERE Email = '{model.Email}'")—— 插值发生在 C# 层,SQL 已被污染 - 注意:如果
model.SortBy用于OrderBy,不能直接OrderBy(x => x.GetType().GetProperty(model.SortBy)),因为字段名不是值,无法参数化 → 必须白名单校验
哪些地方模型绑定“帮不上忙”,必须额外处理
模型绑定能帮你拿到结构化数据,但对 SQL 语法结构位置(非值位置)完全无效。这些地方不靠白名单或硬编码,就等于开门揖盗。
-
ORDER BY后的字段名:model.SortField必须限制为new[] { "name", "email", "created_at" }中的一个 -
IN子句里的多个值:model.Ids是数组,不能拼成"IN (" + string.Join(",", model.Ids) + ")"→ 应该用.Where(u => model.Ids.Contains(u.Id))下推为参数化IN (@p0, @p1, ...) - 表名/列名动态拼接:
FROM {model.TableName}—— 模型绑定毫无意义,只能拒绝或查白名单配置表 -
LIKE模糊查询:u.Name.Contains(model.Keyword)安全;但若手动拼"%"+model.Keyword+"%"再塞进FromSqlRaw,就得自己SqlEscape(),且仍不如 LINQ 下推可靠
最常被忽略的一点:模型绑定再干净,只要后面有一处 string.Format、StringBuilder.Append 或反射取属性名拼 SQL,整个链路就失效。防护不是靠某一个环节,而是所有 SQL 构建点都得过同一道参数化关卡。











