c# - SQL Server 中动态创建的 SQL 与参数

标签 c# sql-server security sql-injection

如果我要从表格中选择一行,我基本上有两个选择,或者像这样

int key = some_number_derived_from_a_dropdown_or_whatever
SqlCommand cmd = new SqlCommand("select * from table where primary_key = " + key.ToString());

或者使用一个参数

SqlCommand cmd = new SqlCommand("select * from table where primary_key = @pk");
SqlParameter param  = new SqlParameter();
param.ParameterName = "@pk";
param.Value         = some_number_derived_from_a_dropdown_or_whatever;
cmd.Parameters.Add(param);

现在,我知道第一种方法不受欢迎,因为可能会受到 sql 注入(inject)攻击,但在这种情况下,参数是一个整数,因此实际上不可能注入(inject)恶意代码。

我的问题是:您是否在生产代码中使用选项 1,因为您认为使用安全是因为易于使用和控制插入的参数(如上所示,或者如果参数是在代码中创建的)?或者无论如何你总是使用参数?参数是否 100% 注入(inject)安全?

最佳答案

我将跳过 SQL 注入(inject)参数,这是众所周知的,只关注参数与非参数的 SQL 方面。

当您向服务器发送 SQL 批处理时,任何批处理都必须经过解析才能被理解。与任何其他编译器一样,SQL 编译器必须生成 AST。从文本中提取,然后对语法树进行操作。最终优化器将语法树转化为执行树,最终生成执行计划并实际运行。回到大约 1995 年的黑暗时代,如果批处理是 Ad-Hoc 查询或存储过程,那会有所不同,但今天它绝对没有,它们都是一样的。

现在参数有所不同的地方在于,客户端每次发送select * from table where primary_key = @pk这样的查询时,都会发送完全相同的SQL文本 ,无论感兴趣的值是什么。然后发生的是我上面描述的整个过程被短路了。 SQL 将在内存中搜索它收到的原始的、未解析的文本(基于输入的哈希摘要)的执行计划,如果找到,将执行该计划。这意味着没有解析,没有优化,什么都没有,批处理将直接执行。在每秒运行成百上千个小请求的 OLTP 系统上,这条快速路径会产生巨大的性能差异。

如果您以 select * from table where primary_key = 1 的形式发送查询,那么 SQL 将必须至少解析它以了解文本中的内容,因为文本可能是新的一个,与它看到的任何之前的批处理都不同(即使像 12 这样的单个字符也会使整个批处理不同)。然后它将对结果语法树进行操作并尝试一个名为 Simple Parameterisation 的过程。 .如果查询可以自动参数化,那么 SQL 可能会从之前使用其他 pk 值运行的其他查询中找到缓存的执行计划并重用该计划,因此至少您的查询不需要优化,您可以跳过生成实际执行计划的步骤。但是,您绝不是实现了完全短路,即通过真正的客户端参数化查询实现的最短路径。

您可以查看 SQL Server, SQL Statistics Object服务器的性能计数器。计数器 Auto-Param Attempts/sec 将每秒显示多次 SQL 必须将收到的不带参数的查询转换为自动参数化的查询。如果您在客户端中正确地参数化查询,则可以避免每次尝试。如果您还有很多 Failed Auto-Params/sec 更糟,这意味着查询正在进行优化和执行计划生成的完整周期。

关于c# - SQL Server 中动态创建的 SQL 与参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1608522/

相关文章:

c# - 使用 C# MVC4 并将 DbContext 类名称与 ConnectionString 名称匹配以使用正确的数据库?

c# - Response.Redirect 抛出错误

sql - 查询性能、索引和预测所覆盖索引的写入时间性能影响?

sql - 如何从类型创建临时表?

sql - 如何在 SQL Server 2008 上安装全文搜索

javascript - 何时转义使用 AJAX 发送的数据

c# - 为什么 BinaryFormatter 会尝试将标记为 [Serializable] 的类型的对象转换为 IConvertible?

c# - 为什么 foreach on PlaceHolder.Controls 抛出 "Collection was modified"?

security - Heroku 和 ip 掩码

php - 安全地存储用户凭证 token