在SQL Server中动态创建SQL vs参数

sin*_*e j 3 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());
Run Code Online (Sandbox Code Playgroud)

或使用参数

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);
Run Code Online (Sandbox Code Playgroud)

现在,我知道第一种方法因为可能的SQL注入攻击而不受欢迎,但在这种情况下,参数是一个整数,因此不应该真正注入恶意代码.

我的问题是:您是否在生产代码中使用选项1,因为您认为使用安全是因为易于使用和控制插入的参数(如上所述,或者参数是否在代码中创建)?或者你总是使用参数,不管是什么?参数100%注射安全吗?

Rem*_*anu 11

我将跳过SQL注入参数,这是众所周知的,只关注参数与非参数的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对象性能计数器.计数器Auto-Param Attempts/sec将每秒显示多次SQL必须将不带参数的查询转换为自动参数化的查询.如果在客户端中正确参数化查询,则可以避免每次尝试.如果您的数量更多Failed Auto-Params/sec甚至更糟,则意味着查询将进行优化和执行计划生成的整个周期.