Blazor应用中SQL注入防护关键在于后端参数化查询:C#代码必须用SqlCommand.Parameters绑定用户输入,禁用字符串拼接;EF Core虽默认参数化,但手写SQL或动态LINQ仍需显式处理;Blazor WebAssembly需确保API层严格遵循此规则。

Blazor服务端组件里直接拼SQL就是高危操作
Blazor Server 或 Blazor WebAssembly(通过后端API)中,只要你在 HttpClient 调用前、或在控制器/服务里手动拼接 string 构造 SQL 查询,比如 "SELECT * FROM Users WHERE Name = '" + name + "'",就等于把数据库大门钥匙交给了用户输入。哪怕加了前端校验、正则过滤,后端照样能被绕过。
C#后端必须用 SqlCommand.Parameters 绑定参数
Blazor本身不处理SQL,真正执行查询的是你后端的 C# 代码(如 WeatherForecastController 或独立的 UserRepository)。防注入的关键动作全在 ADO.NET 层:
- SQL语句中所有用户可控字段必须用
@paramName占位,绝不用字符串插值或+拼接 - 用
cmd.Parameters.Add("@name", SqlDbType.NVarChar).Value = userInput显式声明类型和长度,比AddWithValue更安全——后者可能把空字符串推断成varchar(1),引发隐式转换或截断 - 如果查询涉及整数ID,别用
ToString()再转回字符串传参,直接用cmd.Parameters.Add("@id", SqlDbType.Int).Value = id - 存储过程调用同样适用:用
cmd.CommandType = CommandType.StoredProcedure,再对每个@param调用Add()
Entity Framework Core 的参数化是默认行为,但要注意陷阱
如果你用 EF Core(比如 DbContext.Users.Where(u => u.Name == name)),底层自动生成的 SQL 天然参数化,name 会作为 @__name_0 参数传入——这点很省心。但容易翻车的地方有:
- 手写原始 SQL 时仍需显式参数化:
context.Users.FromSqlRaw("SELECT * FROM Users WHERE Name = {0}", name)是错的;必须用FromSqlRaw("SELECT * FROM Users WHERE Name = {0}", name)的命名参数变体,或更推荐FromSqlInterpolated($"SELECT * FROM Users WHERE Name = {name}") - 动态 LINQ(如
Linq.Dynamic.Core)若拼接表达式树不当,可能绕过参数化,慎用 - EF Core 7+ 支持
ExecuteSqlInterpolated,它能确保插值内容自动转为参数,比ExecuteSqlRaw安全得多
Blazor WebAssembly 不能直连数据库,但 API 层照样要守规矩
WASM 应用运行在浏览器沙箱里,无法直接引用 System.Data.SqlClient 或连接数据库。它必须通过 HttpClient 调用后端 API。这意味着防注入责任完全落在 API 控制器上——不是“Blazor 怎么防”,而是“你的 [ApiController] 里的 GetUser(string name) 方法怎么写”。常见疏忽包括:
- 把
[FromQuery]或[FromBody]接收的值直接塞进string.Format或$"..."拼 SQL - 用 Dapper 时误用
connection.Query(sql, new { name })却把sql变量本身拼出来了(正确做法是sql必须是硬编码常量或资源字符串) - 日志里无意记录完整 SQL(含参数值),造成敏感信息泄露,虽不直接导致注入,但违反最小权限原则
最常被忽略的一点:参数化只解决数据边界问题,不解决逻辑漏洞。比如用户能通过修改 URL 中的 id=123 查到别人的数据,那是水平越权,跟 SQL 注入无关——该加行级权限校验,而不是多加一个 @id 参数。











