参数化查询的关键在于参数值构造方式和通配符位置:like语句须将%写入参数值而非sql字符串,fromsql仅支持{0}占位符绑定,request.params等入口需显式指定来源,复杂查询优先用linq而非手写sql。

直接说结论:参数化查询不是“用了就行”,关键在参数值的构造方式和 SQL 字符串中通配符的位置——LIKE 语句最容易翻车,FromSql 容易误用,Request.Params 这类入口最常被忽略。
LIKE 查询必须把 % 写进参数值里,不能写在 SQL 字符串里
很多人写 "SELECT * FROM User WHERE Name LIKE '%@keyword%'",看着像参数化,实际完全无效。SQL Server 会把 @keyword 当作字面量字符串,而不是变量,导致永远查不到结果。
- ✅ 正确做法:
cmd.Parameters.AddWithValue("@keyword", "%" + userInput + "%"),SQL 字符串写成"SELECT * FROM User WHERE Name LIKE @keyword" - ❌ 错误写法:
"SELECT * FROM User WHERE Name LIKE '%' + @keyword + '%'"或"... LIKE '%@keyword%'"—— 这两种都等于没参数化 - ⚠️ 注意点:如果用户输入本身含
%或_,且业务需要原义匹配,得配合ESCAPE,比如LIKE @pattern ESCAPE '',然后传入"%abc%%"并设cmd.Parameters["@pattern"].Value = "\%abc\%\%"
EF Core 的 FromSql 必须用 {0} 占位符 + 参数列表,不能拼接字符串
FromSql 看似支持字符串格式化,但 "SELECT * FROM Dept WHERE Id = " + id 或 $"SELECT ... WHERE Id = {id}" 都是高危操作。EF Core 的 FromSql 只对 {0}、{1} 这种位置占位符做安全绑定,其余一律当原始 SQL 处理。
- ✅ 安全调用:
_context.Departments.FromSql("SELECT * FROM Department WHERE Id = {0}", id) - ❌ 危险写法:
_context.Departments.FromSql($"SELECT * FROM Department WHERE Id = {id}")—— 编译期不报错,运行时直接注入 - ⚠️ 补充说明:EF Core 5+ 支持命名参数(如
{@id}),但底层仍依赖DbCommand的参数绑定机制,别指望它能自动识别变量名;命名只是可读性优化,不是安全兜底
所有 Request 入口都要统一校验,别只盯 QueryString
开发者常以为 Request.Query["q"] 是唯一风险入口,其实 Request.Params["q"] 更危险——它会按 Query → Form → Cookies → ServerVariables 顺序查找,一旦某处被污染(比如恶意 Cookie),就绕过所有前端过滤。
- ✅ 推荐做法:显式指定来源,例如只取
HttpContext.Request.Query["search"]或HttpContext.Request.Form["search"],禁用Request["key"]和Request.Params["key"] - ❌ 常见疏漏:后台导出接口用
Request["filter"]拼接 SQL,以为“内部系统没人用”,结果被员工浏览器插件或调试工具轻易触发 - ⚠️ 特别注意:MVC 路由参数(如
/user/{id})默认走模型绑定,但如果手动用RouteData.Values["id"]拼 SQL,一样不安全
复杂查询别硬刚 SQL,优先用 LINQ + AsNoTracking
当业务需要动态 WHERE 条件(比如多字段模糊搜索、可选排序),硬写参数化 SQL 很容易漏掉某个分支。EF Core 的 LINQ 查询天然参数化,且编译期就能检查类型安全。
- ✅ 推荐路径:
_context.Users.AsNoTracking().Where(x => x.Name.Contains(keyword)).OrderBy(x => x.CreatedAt),EF 自动生成带参数的 SQL - ❌ 折中陷阱:先拼 LINQ 表达式树再转 SQL 字符串——这一步如果用了
Expression.Constant插入用户输入,等于又回到字符串拼接 - ⚠️ 性能提示:
AsNoTracking()在只读场景下能显著降低内存开销,但别为了省这点性能去换手写 SQL + 参数化
真正难的不是写出第一个参数化查询,而是让每个 Request 取值、每条 LIKE、每个 FromSql 调用、甚至每个历史遗留的 Response.Write 输出,都保持同一套参数构造逻辑。安全不是加个 @param 就完事,是让参数值从进入应用那一刻起,就再没机会被当成 SQL 代码执行。











