dapper通过@param占位符+匿名对象或dynamicparameters可机制级防止sql注入;需白名单校验order by/group by列名、表名等非参数化场景。

只要参数走 @param 占位符 + 匿名对象或 DynamicParameters,Dapper 就不会产生 SQL 注入——这是机制级保障,不是靠运气。
什么时候必须用匿名对象传参
绝大多数查询场景下,匿名对象是最简、最安全的选择。它强制你把值和 SQL 模板彻底分离,连类型推导都由 Dapper 自动完成。
-
WHERE条件里的字段值:比如写"SELECT * FROM Users WHERE Email = @Email",就配new { Email = userInput } -
IN子句的值列表:传new { Ids = new[] { 1, 2, 3 } },Dapper 自动展开为WHERE Id IN (@Ids0, @Ids1, @Ids2) -
LIKE模糊匹配:写"Name LIKE @name",然后传new { name = $"%{search}%" };千万别拼$"Name LIKE '%{search}%'" - 多条件动态组合时,哪怕只用部分字段,也比拼 SQL 安全:比如
if (status != null) sql += " AND Status = @Status",再统一传new { status }
什么时候非得换 DynamicParameters
匿名对象够用,但遇到这几类情况,它就无能为力,必须上 DynamicParameters。
- 需要输出参数:比如存储过程返回
@TotalCount,就得用Add("@TotalCount", dbType: DbType.Int32, direction: ParameterDirection.Output) - 要传表值参数(
DataTable或自定义IDynamicParameter):匿名对象无法表达结构化表数据 - 参数类型模糊:像
DateTimeOffset、Guid在某些驱动(如早期Npgsql)下可能被误判为string,需显式指定dbType - 参数名动态生成:比如根据请求字段决定加
@CreatedAfter还是@UpdatedBefore,得用Add()逐个注册
注意:DynamicParameters 本身不增加风险——只要你不调用 AddWithValue 去拼字符串、不把用户输入塞进 SQL 字符串里,它仍是参数化的。
哪些地方根本不能参数化,必须另做防护
@param 只能替值,不能替语法结构。以下三类必须隔离处理,否则再怎么用 DynamicParameters 都没用:
-
ORDER BY后的列名:数据库不支持参数化排序字段,必须白名单校验,比如if (!new[] { "Name", "CreatedAt" }.Contains(sortField)) throw -
GROUP BY、HAVING中的列名:同上,不能靠@col替换 - 表名、视图名、schema 名:比如
FROM @tableName是非法语法,必须用字符串拼接,但只能来自配置或枚举,绝不能来自用户输入
这类地方一旦放开自由输入,等于直接绕过所有参数化机制——Dapper 对它完全无感,也不会报错,只会执行恶意 SQL。










