导读:本期聚焦于小伙伴创作的《如何解决CSharp Dapper框架下的SQL注入风险_强制使用匿名对象传递参数》,敬请观看详情。把拼接字符串直接塞进Dapper查询,是生产环境里最常见也最危险的做法。一次普通的搜索接口,只要把用户输入原样拼进SQL,攻击者就能用单引号闭合语句并注入恶意的or 1=1。Dapper本身不会替你拦截这种写法,它只负责把命令发给数据库。真正能拦住注入的,是改用匿名对象把参数交给Dapper,由驱动层把值与指令分开传输。下面从原理、写法和常见误区三方面说明,为什么强制使用匿名对象传参可以彻底规避这类风险,以及旧代码该怎么改。

在C#项目里用Dapper做数据访问时,开发者常常图省事把查询条件和用户输入拼成完整SQL字符串。这种做法在Dapper中并不会被自动净化,数据库收到的仍是一条拼接好的指令,从而留下SQL注入缺口。强制使用匿名对象传递参数,可以让Dapper把参数值作为独立数据发送给数据库引擎,从机制上切断注入路径。

如何解决CSharp Dapper框架下的SQL注入风险_强制使用匿名对象传递参数

为什么拼接SQL会在Dapper中导致注入

Dapper是一个轻量级的ORM,它的核心职责是把C#对象映射成数据库命令并执行。它并不对传入的SQL字符串做语义分析或危险字符过滤。当你写出类似string.Format("select * from user where name='{0}'", input)这样的代码时,Dapper只是原样把这段字符串交给ADO.NET。如果input内容是admin' or '1'='1,最终发往数据库的指令就变成了两条逻辑被篡改的查询条件。

不少团队误以为用了Dapper就天然安全,这是典型的概念混淆。Dapper的安全边界取决于你如何传参,而不是用了哪个ORM。只要SQL文本里混入了外部输入,就绕过了参数化查询的保护。下面这段有问题的代码展示了危险写法:

string userName = Request.QueryString["name"];
string sql = "select id, name from users where name = '" + userName + "'";
using (var conn = new SqlConnection(connStr))
{
    var list = conn.Query(sql).ToList();
}

上面代码里userName来自请求参数,直接与SQL拼接。攻击者传入' drop table users --就能造成破坏。这种写法在本地测试时一切正常,上线后却成为最容易被扫描利用的弱点。

匿名对象传参如何阻断注入

匿名对象在C#里用new { 属性名 = 值 }的形式创建,Dapper会把它每一个属性识别为一个参数,并通过DbCommand的Parameter集合发给数据库。数据库收到的是带占位符的指令和独立的参数值,两者在协议层分离,值不会被当作SQL语法解析。即使值里包含单引号或分号,也仅仅是字符串内容。

使用匿名对象时,SQL里用@符号声明参数名,Dapper按属性名自动匹配。下面是正确的改写方式:

string userName = Request.QueryString["name"];
string sql = "select id, name from users where name = @name";
using (var conn = new SqlConnection(connStr))
{
    var list = conn.Query(sql, new { name = userName }).ToList();
}

这里@name对应匿名对象的name属性。无论userName是什么,数据库都只把它当作用户名去比较。这种方式既简洁又安全,不需要手动创建SqlParameter,也不会因为漏写引号而出错。

强制规范的落地与旧代码改造

要在团队里强制使用匿名对象,首先可以在代码评审清单中把拼接SQL列为禁止项。对于已有项目,可以写一个小工具扫描源码中Query、Execute方法调用,检查其SQL参数是否为常量加变量拼接。发现一处改一处,成本通常不高。

当查询条件动态变化时,也不要退回拼接,而是用匿名对象配合条件拼接参数对象。例如列表筛选有多个可选条件:

var sql = "select * from orders where 1=1";
var param = new DynamicParameters();
if (!string.IsNullOrEmpty(status))
{
    sql += " and status = @status";
    param.Add("status", status);
}
if (minPrice.HasValue)
{
    sql += " and price >= @minPrice";
    param.Add("minPrice", minPrice.Value);
}
using (var conn = new SqlConnection(connStr))
{
    var data = conn.Query(sql, param);
}

上例用了Dapper的DynamicParameters,它本质也是参数对象,同样安全。关键是任何外部值都通过Add方法进入参数集合,而不是直接进SQL字符串。这样动态查询也能保持零注入风险。

常见误区与补充说明

有人认为对输入做转义替换单引号就足够了,这并不可靠。不同数据库对转义规则不同,且容易漏掉二次注入场景。参数化才是通用解。还有人担心匿名对象性能差,实际上Dapper对匿名对象有缓存机制,首次反射后后续调用几乎无额外开销。

最后提醒,存储过程调用、like查询也同样适用匿名对象。like的百分号应写在参数值里而不是SQL中:new { key = "%" + input + "%" },SQL写like @key即可。只要坚持所有值走参数,Dapper下的SQL注入风险就能被强制消除。

DapperSQL注入匿名对象修改时间:2026-08-07 23:51:28

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。