如果我要选择一个表中的行我基本上有两种选择,要么喜欢这个

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注射攻击,但在这种情况下的参数是一个整数和因此不应该真正能够注入恶意代码。

我的问题是这个:你用备选案文1中的产生的代码,因为你考虑使用安全的,因为便于使用和控制插入参数(如上述,或者如果参数是建立在代码)?或者你总是使用参数的无论什么?是参数的100%,注射安全吗?

有帮助吗?

解决方案

我将跳过SQL Injection参数,这个参数已经过于广为人知,只关注参数与非参数的SQL方面。

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

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

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

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

其他提示

始终使用选项2)。

关于 第一个 安全rel =“nofollow noreferrer”> SQL注入攻击

第二个 不仅更安全,而且还会表现更好,因为查询优化器有更好的机会为它创建一个好的查询计划,因为查询看起来同样一直都在期待参数。

为什么要避免使用选项1

即使您从 select 元素中获取此值,但有人可能伪造HTTP请求并在某些字段中发布他们想要的任何内容。选项1中的代码至少应该替换一些危险的字符/组合(即单引号,方括号等)。

为什么鼓励您使用选项2

SQL查询引擎能够缓存执行计划并管理为其抛出的各种查询的统计信息。因此,统一查询(最好的一个当然是拥有一个单独的存储过程)从长远来看会加快执行速度。

我还没有听说过任何可以为SQL注入劫持参数的示例。直到我看到它证明不然,我认为它们是安全的。

永远不应将动态SQL视为安全。即使是“受信任的”输入,这是一个坏习惯进入。请注意,这包括存储过程中的动态SQL; SQL注入也可以在那里发生。

因为你有控制的价值是一个整数,他们几乎是等同的。我一般不使用的第一种形式,并且通常,我不允许第二,因为我通常不允许表访问和需要使用储存的程序。虽然你可以做SP执行而不使用参数的集合,我还是建议使用SPs 参数:

-- Not vulnerable to injection as long as you trust int and int.ToString()
int key = some_number_derived_from_a_dropdown_or_whatever ;
SqlCommand cmd = new SqlCommand("EXEC sp_to_retrieve_row " + key.ToString()); 

-- Vulnerable to injection all of a sudden
string key = some_number_derived_from_a_dropdown_or_whatever ;
SqlCommand cmd = new SqlCommand("EXEC sp_to_retrieve_row " + key.ToString()); 

注意到尽管备选案文1是安全的 在你的情况,会发生什么,当有人看到和使用这种技术与非整数的变现在你是培训他人使用一种技术, 可能是 开放注射。

注意,你可以积极主动地强化数据库,以避免注是有效的,甚至当你的应用程序的代码是错误的。帐户/角色应用程序使用:

  • 只允许访问表作为绝对必要的
  • 只允许访问视为绝对必要的
  • 不允许言发言
  • 允许执行以SPs上的作用的基础
  • 任何的动态SQL在SPs应该审查是绝对必要的

就个人而言,我总是习惯使用选项2。话虽如此:

由于您强制从下拉值转换为int,这将提供一些防止sql注入攻击的保护。有人试图在发布的信息中注入额外的信息,C#会在尝试将恶意代码转换为阻止尝试的整数值时抛出异常。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top