仔细考虑如果有人在您的应用程序的城市文本框中输入以下内容会发生什么:
';从客户中删除;--
你最终会构造一个如下所示的 sql 字符串:
select * from Customers where city = '';DELETE FROM Customers;--'
碰巧有两条语句和一条注释,您的 sql 提供程序非常乐意执行它们。现在,数据库连接也可能以用户身份运行,没有从该表中删除所需的权限......但它可能具有其他权限,例如从其他表中读取数据以获取社会安全号码或信用卡等信息,或者也许插入具有管理权限的新帐户记录。熟练的攻击可以用它来做几乎任何事情。
编写一个简单的转义或清理函数可能很诱人。不要这样做。首先,它并没有完全解决问题。查询参数将用户数据与 sql 代码完全隔离,因此即使是数据库也始终将用户输入视为变量值,而从不将其直接放入查询字符串中。使用逃生功能会让您与饼干进行军备竞赛。
另一个原因是性能。数据库在将查询转化为如何检索数据的执行计划方面遇到了很多麻烦。如此之多,以至于他们经常以查询文本的散列作为键来缓存计划。如果您不使用参数化查询,那么每次都会导致缓存未命中。
最后,我发现参数化查询更容易使用。对于复杂的查询,我可能会在查询上方添加注释,如下所示:
/*DECLARE @someVariable varchar(12);
DECLARE @OtherVariable varchar(50);
DECLARE @thirdVariable int;
...*/
var sql =
"SELECT <columns>
FROM table t"
INNER JOIN othertable o ON t.ID = o.tableID"
WHERE someColumn = @someVariable AND ...";
using (var cn = new SqlConnection(" ... "))
using (var cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.Add()
//...
这样做的目的是在需要维护此查询时,我可以快速将查询及其变量声明复制/粘贴到 Management Studio 或其他查询工具中,完成后我可以快速将整个混乱移回客户端代码。对于一个班轮来说,这没什么大不了的,真正巨大/复杂的查询通常最终会成为存储过程或视图,但是有一个真正有用的甜蜜点..结果更多的事情最终出现在那个甜蜜点比你想象的要好。