不安全。动态OrderBy/Select字段名会导致内存排序、反射异常或表达式树注入;必须用预定义映射字典白名单校验,禁用GetProperty和Expression.Parameter拼路径。

动态OrderBy/Select字段名是否安全?
不安全。LINQ本身不接受运行时字符串作为表达式路径,OrderBy(x => x.GetType().GetProperty(sortField).GetValue(x))这类写法会强制切换到内存排序,既无法下推SQL,又可能因反射失败抛异常。更危险的是用Expression.Property拼表达式树——若sortField未严格白名单校验,攻击者可传入"Password"或"AdminFlag"等敏感属性名,绕过编译期检查。
真正可行的方案是预定义映射字典:
- 建立
Dictionary<string expression object>>></string>,键为前端允许的字段名(如"name"、"email"),值为对应强类型表达式(如u => u.Name) - 用户传
sort=name时只查字典,不存在则返回400或默认排序 - 禁止任何基于
typeof(T).GetProperty()或Expression.Parameter构造路径的操作
FromSqlRaw和ExecuteSqlRaw怎么审计才有效?
这是.NET项目中最常被漏审的高危点。审计时不能只看有没有“用了EF Core”,而要逐行确认是否出现$"SELECT * FROM Users WHERE Name = '{input}'"这类插值写法——哪怕只拼一个变量,也等于直接执行恶意SQL。
合规写法只有两种:
- 位置占位符:
context.Users.FromSqlRaw("SELECT * FROM Users WHERE Email = {0}", email) - 命名参数(SQL Server):
context.Users.FromSqlRaw("SELECT * FROM Users WHERE Email = @email", new SqlParameter("@email", email)) - 绝对禁用
Parameters.AddWithValue(),它会错误推断类型导致索引失效
审计脚本可 grep FromSqlRaw\(|ExecuteSqlRaw\(,再人工核对所有参数是否全为占位符形式,无字符串拼接痕迹。
AsEnumerable() + Where能防注入吗?
不能。这只会把整张表拉到内存再过滤,数据量稍大就OOM,且完全没解决注入源头问题。如果前面已经用FromSqlRaw执行了带用户输入的原始SQL,那注入早在数据库层就完成了。
这种写法只适用于:本地调试、极小静态数据集(
典型误用:
var users = context.Users.AsEnumerable().Where(u => u.Name.Contains(searchTerm));
正确替代:
var users = context.Users.Where(u => u.Name.Contains(searchTerm));
表名/列名动态拼接如何审计?
参数化只对值有效,对标识符(表名、列名、排序方向ASC/DESC)无效。审计时重点抓三类代码模式:
-
string tableName = Request.Query["table"]; context.Set<object>().FromSqlRaw($"SELECT * FROM [{tableName}]")</object>→ 必须改用白名单校验或QUOTENAME()转义 -
ORDER BY {sortField} {sortDir}→sortField只能从预设列表取,sortDir只允许"ASC"或"DESC"字面量 - 存储过程中调用
EXEC(@sql)→ 审计SP源码,确认内部未拼接用户输入
最容易被忽略的是日志导出、多租户分表、BI报表类功能——这些地方往往绕过ORM,直连SQL,也是注入重灾区。

















