Dapper通过@参数占位符绑定值可机制级防SQL注入;但ORDER BY、GROUP BY、表名等非参数化位置必须白名单校验,否则存在高危漏洞。

只要参数走 @param 占位符、值不拼进 SQL 字符串,Dapper 本身就不会产生 SQL 注入——这是机制级保障,不是靠经验或运气。
用 StringBuilder 拼 WHERE 条件 + DynamicParameters 绑定值
这是最可控、最易调试的方式,尤其适合条件分支多、逻辑复杂的真实业务场景。
- 以
"WHERE 1=1"开头,后续每个条件统一用AND追加,避免判断首条件是否带WHERE - 每个条件对应一个唯一参数名(如
@name、@minPrice),用DynamicParameters.Add()显式注册,不依赖匿名对象的自动推导 - 绝不把用户输入塞进字符串插值或
$"":错误写法builder.Append($"AND Name LIKE '%{input}%'");正确写法builder.Append("AND Name LIKE @name"); params.Add("@name", $"%{input}%") - 示例片段:
var sql = new StringBuilder("SELECT * FROM Products WHERE 1=1");<br>var parameters = new DynamicParameters();<br>if (!string.IsNullOrEmpty(name)) {<br> sql.Append(" AND Name LIKE @name");<br> parameters.Add("@name", $"%{name}%");<br>}<br>if (minPrice > 0) {<br> sql.Append(" AND Price >= @minPrice");<br> parameters.Add("@minPrice", minPrice);<br>}
用 Dapper.SqlBuilder 管理模板与占位符
它把 SQL 结构和动态部分解耦,适合需要复用查询结构(如分页、排序)的场景,但要注意它不自动防注入——安全仍靠你控制参数绑定。
- 必须用
AddTemplate()定义占位符(如/**where**/),再调用Where()方法注入条件,不能直接拼字符串到模板里 -
Where()的第二个参数必须是匿名对象或DynamicParameters,否则参数不会被识别:错误写法.Where("Status = " + status);正确写法.Where("Status = @status", new { status }) - 占位符只支持预定义类型:
/**where**/、/**orderby**/、/**select**/等,自定义占位符无效 - 示例:
var builder = new SqlBuilder();<br>var template = builder.AddTemplate("SELECT * FROM Orders /**where**/ /**orderby**/");<br>if (userId > 0) builder.Where("UserId = @userId", new { userId });<br>if (!string.IsNullOrEmpty(status)) builder.Where("Status = @status", new { status });<br>builder.OrderBy("CreatedAt DESC");<br>var results = conn.Query<Order>(template.RawSql, template.Parameters);
哪些地方 Dapper 根本不防注入,必须手动白名单校验
@param 只能替值,不能替语法结构。一旦在这三类地方放开用户输入,Dapper 不报错、不拦截,直接执行恶意 SQL。
-
ORDER BY后的列名:数据库语法不支持ORDER BY @col,必须校验输入是否在允许列表内,例如if (!new[] { "Name", "CreatedAt", "Amount" }.Contains(sortBy)) throw new ArgumentException() -
GROUP BY或HAVING中的字段名:同上,不能参数化,只能白名单 - 表名、视图名、schema 名:像
FROM @tableName是非法 SQL,必须来自配置、枚举或硬编码,绝不可取自Request.Query["table"]
真正容易被忽略的是:ORDER BY / GROUP BY / 表名这些“非参数化位置”,开发者常误以为用了 Dapper 就万事大吉,结果在这些地方埋下高危漏洞——它们完全绕过 Dapper 的参数机制,且没有任何运行时提示。

















