当参数不是字符串时,不参数化SQL查询是否安全?

时间:2022-02-05 03:30:50

In terms of SQL injection, I completely understand the necessity to parameterize a string parameter; that's one of the oldest tricks in the book. But when can it be justified to not parameterize an SqlCommand? Are any data types considered "safe" to not parameterize?

在SQL注入方面,我完全理解对字符串参数进行参数化的必要性;这是书中最古老的把戏之一。但是什么时候可以不参数化SqlCommand呢?是否有任何数据类型被认为是“安全的”而非参数化?

For example: I don't consider myself anywhere near an expert in SQL, but I can't think of any cases where it would be potentially vulnerable to SQL injection to accept a bool or an int and just concatenate it right into the query.

例如:我不认为自己是SQL方面的专家,但我想不出任何可能容易受到SQL注入影响的情况,即接受bool或int并将其连接到查询中。

Is my assumption correct, or could that potentially leave a huge security vulnerability in my program?

我的假设是正确的,还是可能会在我的程序中留下一个巨大的安全漏洞?

For clarification, this question is tagged which is a strongly-typed language; when I say "parameter," think something like public int Query(int id).

为了澄清,这个问题被标记为c#,这是一种强类型语言;当我说“参数”时,请考虑公共int查询(int id)。

12 个解决方案

#1


100  

I think it's safe... technically, but it's a terrible habit to get into. Do you really want to be writing queries like this?

我认为这是安全的……从技术上来说,这是一个可怕的习惯。你真的想写这样的查询吗?

var sqlCommand = new SqlCommand("SELECT * FROM People WHERE IsAlive = " + isAlive + 
" AND FirstName = @firstName");

sqlCommand.Parameters.AddWithValue("firstName", "Rob");

It also leaves you vulnerable in the situation where a type changes from an integer to a string (Think employee number which, despite its name - may contain letters).

当一个类型从一个整数变为一个字符串时(请考虑员工编号,尽管它的名称是name的),它可能包含字母。

So, we've changed the type of EmployeeNumber from int to string, but forgot to update our sql queries. Oops.

因此,我们已经将EmployeeNumber类型从int改为string,但是忘记了更新sql查询。哦。

#2


65  

When using a strongly-typed platform on a computer you control (like a web server), you can prevent code injection for queries with only bool, DateTime, or int (and other numeric) values. What is a concern are performance issues caused by forcing sql server to re-compile every query, and by preventing it from getting good statistics on what queries are run with what frequency (which hurts cache management).

当在您控制的计算机(如web服务器)上使用强类型平台时,您可以防止只使用bool、DateTime或int(和其他数值)值的查询的代码注入。令人关注的是强制sql server重新编译每个查询所导致的性能问题,并防止它获得关于哪些查询以什么频率运行的良好统计信息(这会损害缓存管理)。

Really, there's no good reason not to use query parameters for these values. It's the right way to go about this. Go ahead and hard-code values into the sql string when they really are constants, but otherwise, why not just use a parameter? It's not like it's hard.

实际上,没有理由不使用这些值的查询参数。这是正确的做法。当sql字符串实际上是常量时,继续对其进行硬编码,否则,为什么不使用参数呢?这并不难。

I also like to think long-term. What happens when today's old-and-busted strongly-typed code base gets ported via automatic translation to the new-hotness dynamic language, and you suddenly lose the type checking, but don't have all the right unit tests yet for the dynamic code?

我也喜欢长远考虑。当今天的旧式强类型代码库通过自动转换到新热点动态语言进行移植时,您突然丢失了类型检查,但还没有对动态代码进行所有正确的单元测试时,会发生什么情况?

Ultimately, I wouldn't call this a bug, per se, but I would call it a smell: something that falls just short of a bug by itself, but is a strong indication that bugs are nearby, or will be eventually. Good code avoids leaving smells, and any good static analysis tool will flag this.

最终,我不会把它本身称为bug,但我会把它称为一种气味:一种本身没有bug的东西,但它强烈地表明bug就在附近,或者最终会存在。好的代码避免留下气味,任何好的静态分析工具都会标记这一点。

I'll add that this is not, unfortunately, the kind of argument you can win straight up. It sounds like a situation where being "right" is no longer enough, and stepping on your co-workers toes to fix this issue on your own isn't likely to promote good team dynamics; it could ultimately hurt more than it helps. A better approach in this case may be to promote the use of a static analysis tool. That would give legitimacy and credibility to efforts aimed and going back and fixing existing code.

我要补充一点,不幸的是,这不是那种你可以直接赢得的论点。这听起来似乎是一种“正确”已经不再足够的情况,你自己去解决这个问题也不太可能促进良好的团队活力;它最终可能造成的伤害比帮助更大。在这种情况下,更好的方法可能是促进使用静态分析工具。这将使旨在恢复和修正现有代码的努力具有合法性和可信性。

#3


53  

In some cases, it IS possible to perform SQL injection attack with non-parametrized (concatenated) variables other than string values - see this article by Jon: http://codeblog.jonskeet.uk/2014/08/08/the-bobbytables-culture/ .

在某些情况下,可以使用非参数化的(连接的)变量来执行SQL注入攻击,而不是字符串值——请参见Jon: http://codeblog.jonskeet。英国/ 2014/08/08 / the-bobbytables-culture /。

Thing is that when ToString is called, some custom culture provider can transform a non-string parameter into its string representation which injects some SQL into the query.

当调用ToString时,一些自定义区域性提供程序可以将非字符串参数转换为字符串表示形式,从而向查询注入一些SQL。

#4


47  

This is not safe even for non-string types. Always use parameters. Period.

即使是非字符串类型也不安全。总是使用参数。时期。

Consider following code example:

考虑下面的代码示例:

var utcNow = DateTime.UtcNow;
var sqlCommand = new SqlCommand("SELECT * FROM People WHERE created_on <= '" + utcNow + "'");

At the first glance code looks safe, but everything changes if you make some changes in Windows Regional Settings and add injection in short date format:

乍一看,代码看起来很安全,但如果你对Windows区域设置做了一些更改,并以短日期格式添加注入,一切都会改变:

当参数不是字符串时,不参数化SQL查询是否安全?

Now resulting command text looks like this:

现在生成的命令文本如下所示:

SELECT * FROM People WHERE created_on <= '26.09.2015' OR '1'<>' 21:21:43'

The same can be done for int type as user can define custom negative sign which can be easily changed into SQL injection.

对于int类型也可以这样做,因为用户可以定义自定义的负号,可以很容易地将其转换为SQL注入。

One could argue that invariant culture should be used instead of current culture, but I have seen string concatenations like this so many times and it is quite easy to miss when concatenating strings with objects using +.

有人可能会说应该使用不变文化而不是当前文化,但是我已经见过很多次像这样的字符串连接,当使用+连接字符串时很容易被忽略。

#5


24  

"SELECT * FROM Table1 WHERE Id=" + intVariable.ToString()

"SELECT * FROM Table1, Id=" + intVariable.ToString()


Security
It is OK.
Attackers can not inject anything in your typed int variable.

安全就可以了。攻击者不能在类型int变量中插入任何内容。

Performance
Not OK.

性能不好。

It's better to use parameters, so the query will be compiled once and cached for next usage. Next time even with different parameter values, query is cached and doesn't need to compile in database server.

最好使用参数,因此查询将被编译一次并缓存以备下次使用。下次即使使用不同的参数值,查询也会被缓存,不需要在数据库服务器中编译。

Coding Style
Bad practice.

编码风格不好的实践。

  • Parameters are more readable
  • 参数更具可读性
  • Maybe it makes you get used to queries without parameters, then maybe you made a mistake once and use a string value this way and then you probably should say goodbye to your data. Bad habit!
  • 也许它让你习惯了没有参数的查询,然后也许你犯了一个错误,用这种方式使用一个字符串值,然后你可能应该对你的数据说再见。坏习惯!


"SELECT * FROM Product WHERE Id=" + TextBox1.Text

“从Id=”+ TextBox1.Text的产品中选择*


Although it is not your question, but maybe useful for future readers:

虽然这不是你的问题,但可能对未来的读者有用:

Security
Disaster!
Even when the Id field is integer, your query may be subject to SQL Injection. Suppose you have a query in your application "SELECT * FROM Table1 WHERE Id=" + TextBox1.Text, An attacker can insert into text box 1; DELETE Table1 and the query will be:

安全灾难!即使Id字段是整数,您的查询也可能受到SQL注入的影响。假设您的应用程序“SELECT * FROM Table1”中有一个查询,其中Id=" + TextBox1。攻击者可以在文本框1中插入;删除表1,查询为:

"SELECT * FROM Table1 WHERE Id=1; DELETE Table1"

If you don't want to use parametrized query here, you should use typed values:

如果您不想在这里使用参数化查询,您应该使用类型化值:

string.Format("SELECT * FROM Table1 WHERE Id={0}", int.Parse(TextBox1.Text))


Your Question

你的问题


My question arose because a coworker wrote a bunch of queries concatenating integer values, and I was wondering whether it was a waste of my time to go through and fix all of them.

我的问题出现是因为一位同事写了一串连接整数值的查询,我想知道我是否在浪费时间去检查并修复它们。

I think changing those codes is not waste of time. Indeed change is Recommended!

我认为改变这些代码不是浪费时间。事实上改变推荐!

if your coworker uses int variables, it has no security risk , But I think changing those codes is not waste of time and indeed changing those codes is recommended. It makes code more readable, more maintainable and makes execution faster.

如果您的同事使用int变量,则没有安全风险,但是我认为更改这些代码并不浪费时间,确实建议更改这些代码。它使代码更具可读性,更易于维护,并且使执行速度更快。

#6


19  

There are actually two questions in one. And question from the title has very little to do with concerns expressed by the OP in the comments afterwards.

实际上有两个问题。题目中的问题与OP在后面的评论中表达的担忧关系不大。

Although I realize that for the OP it is their particular case that matters, for the readers coming from Google, it is important to answer to the more general question, that can be phrased as "is concatenation as safe as prepared statements if I made sure that every literal I am concatenating is safe?". So, I would like to concentrate on this latter one. And the answer is

虽然我知道OP才是他们的具体情况,为读者来自谷歌,重要的是要回答到更一般的问题,可以以“是连接一样安全准备好的语句如果我确信每一个文字连接是安全的?”。因此,我想把注意力集中在后一个问题上。答案是

Definitely NO.

The explanation is not that direct as most readers would like, but I'll try my best.

这种解释并不像大多数读者所想的那样直接,但我会尽我最大的努力。

I have been pondering on the matter for a while, resulting in the article (though based on the PHP environment) where I tried to sum everything up. It occurred to me that the question of protection from SQL injection is often eludes toward some related but narrower topics, like string escaping, type casting and such. Although some of the measures can be considered safe when taken by themselves, there is no system, nor a simple rule to follow. Which makes it very slippery ground, putting too much on the developer's attention and experience.

我已经对这个问题思考了一段时间,并在本文中(尽管基于PHP环境)尝试对所有内容进行总结。我突然想到,保护不受SQL注入影响的问题常常回避一些相关但较窄的主题,如字符串转义、类型转换等。虽然有些措施本身可以被认为是安全的,但没有制度,也没有简单的规则可以遵循。这使它变得非常滑,给开发人员太多的注意力和经验。

The question of SQL injection cannot be simplified to a matter of some particular syntax issue. It is wider than average developer used to think. It's a methodological question as well. It is not only "Which particular formatting we have to apply", but "How it have to be done" as well.

SQL注入的问题不能简化为某些特定语法问题。它比一般的开发人员想象的要宽。这也是一个方法论的问题。它不仅是“我们必须应用的特定格式”,还包括“如何完成”。

(From this point of view, an article from Jon Skeet cited in the other answer is doing rather bad than good, as it is again nitpicking on some edge case, concentrating on a particular syntax issue and failing to address the problem at whole.)

(从这个角度来看,另一个答案中引用的Jon Skeet的一篇文章做得相当糟糕,而不是很好,因为它再次挑剔某些edge案例,专注于特定的语法问题,而未能完全解决问题。)

When you're trying to address the question of protection not as whole but as a set of different syntax issues, you're facing multitude of problems.

当您试图解决保护问题而不是作为一组不同的语法问题时,您将面临大量的问题。

  • the list of possible formatting choices is really huge. Means one can easily overlook some. Or confuse them (by using string escaping for identifier for example).
  • 可能的格式选择列表非常巨大。意思是一个人可以很容易地忽略一些。或者混淆它们(例如使用字符串转义作为标识符)。
  • Concatenation means that all protection measures have to be done by the programmer, not program. This issue alone leads to several consequences:
    • such a formatting is manual. Manual means extremely error prone. One could simply forget to apply.
    • 这种格式是手动的。手动意味着极容易出错。你可以简单地忘记申请。
    • moreover, there is a temptation to move formatting procedures into some centralized function, messing things even more, and spoiling data that is not going to database.
    • 此外,还有一种诱惑,即将格式化过程转移到某个集中函数中,从而使事情变得更混乱,并破坏不进入数据库的数据。
  • 连接意味着所有的保护措施必须由程序员完成,而不是程序。仅这个问题就会导致几个后果:这种格式是手动的。手动意味着极容易出错。人们可能会忘记申请。此外,还有一种诱惑,即将格式化过程转移到某个集中函数中,从而使事情变得更混乱,并破坏不进入数据库的数据。
  • when more than one developers involved, problems multiply by a factor of ten.
  • 当涉及多个开发人员时,问题会乘以10倍。
  • when concatenation is used, one cannot tell a potentially dangerous query at glance: they all potentially dangerous!
  • 当使用连接时,不能一眼就看出一个潜在危险的查询:它们都有潜在危险!

Unlike that mess, prepared statements are indeed The Holy Grail:

与那种混乱不同,事先准备好的声明确实是圣杯:

  • it can be expressed in the form of one simple rule that is easy to follow.
  • 它可以用一个简单的规则来表示,这个规则很容易遵循。
  • it is essentially undetacheable measure, means the developer cannot interfere, and, willingly or unwillingly, spoil the process.
  • 它本质上是不可分离的度量,这意味着开发人员不能进行干预,并且愿意或不情愿地破坏这个过程。
  • protection from injection is really only a side effect of the prepared statements, which real purpose is to produce syntactically correct statement. And a syntactically correct statement is 100% injection proof. Yet we need our syntax to be correct despite of any injection possibility.
  • 对注入的保护实际上只是准备语句的副作用,其真正目的是生成语法正确的语句。语法正确的语句是100%注射证明。然而,我们需要我们的语法是正确的,尽管有任何注入的可能性。
  • if used all the way around, it protects the application regardless of the developer's experience. Say, there is a thing called second order injection. And a very strong delusion that reads "in order to protect, Escape All User Supplied Input". Combined together, they lead to injection, if a developer takes the liberty to decide, what needs to be protected and what not.
  • 如果一直使用,不管开发人员的经验如何,它都会保护应用程序。有一种东西叫做二阶注入。一种非常强烈的错觉,写着“为了保护、逃避所有用户提供的输入”。结合在一起,它们将导致注入,如果开发人员可以*地决定,哪些需要保护,哪些不需要。

(Thinking further, I discovered that current set of placeholders is not enough for the real life needs and have to be extended, both for the complex data structures, like arrays, and even SQL keywords or identifiers, which have to be sometimes added to the query dynamically too, but a developer is left unarmed for such a case, and forced to fall back to string concatenation but that's a matter of another question).

(进一步思考,我发现现在的占位符是不够现实生活需求和扩展,对复杂的数据结构,数组,甚至SQL关键字或标识符,有时需要动态添加到查询中,但开发人员离开手无寸铁的这种情况下,和*回到字符串连接,但这是另一个问题的问题)。

Interestingly, this question's controversy is provoked by the very controversial nature of Stack Overflow. The site's idea is to make use of particular questions from users who ask directly to achieve the goal of having a database of general purpose answers suitable for users who come from search. The idea is not bad per se, but it fails in a situation like this: when a user asks a very narrow question, particularly to get an argument in a dispute with a colleague (or to decide if it worth to refactor the code). While most of experienced participants are trying to write an answer, keeping in mind the mission of Stack Overflow at whole, making their answer good for as many readers as possible, not the OP only.

有趣的是,这个问题的争议是由Stack Overflow的极具争议性引起的。该网站的想法是利用用户直接提出的特定问题,以实现拥有适合搜索用户的通用答案数据库的目标。这个想法本身并不坏,但在这种情况下它就失败了:当用户问一个非常狭窄的问题时,尤其是在与同事发生争执时(或者决定是否值得重构代码)。当大多数有经验的参与者都在尝试编写一个答案时,请记住Stack Overflow的使命,使他们的答案对尽可能多的读者有效,而不仅仅是OP。

#7


16  

Let's not just think about security or type-safe considerations.

让我们不只是考虑安全性或类型安全的考虑。

The reason you use parametrized queries is to improve performance at the database level. From a database perspective, a parametrized query is one query in the SQL buffer (to use Oracle's terminology although I imagine all databases have a similar concept internally). So, the database can hold a certain amount of queries in memory, prepared and ready to execute. These queries do not need to be parsed and will be quicker. Frequently run queries will usually be in the buffer and will not need parsing every time they are used.

使用参数化查询的原因是为了提高数据库级别的性能。从数据库的角度来看,参数化查询是SQL缓冲区中的一个查询(使用Oracle的术语,尽管我认为所有数据库内部都有类似的概念)。因此,数据库可以在内存中保存一定数量的查询,并准备好执行。这些查询不需要解析,而且会更快。经常运行的查询通常位于缓冲区中,每次使用它们时都不需要解析。

UNLESS

除非

Somebody doesn't use parametrized queries. In this case, the buffer gets continually flushed through by a stream of nearly identical queries each of which needs to be parsed and run by the database engine and performance suffers all-round as even frequently run queries end up being re-parsed many times a day. I have tuned databases for a living and this has been one of the biggest sources of low-hanging fruit.

有些人不使用参数化查询。在这种情况下,缓冲区会被几乎相同的查询流不断刷新,每个查询都需要被解析并由数据库引擎运行,性能会受到全面影响,因为即使是频繁运行的查询,也会被每天多次重新解析。我已经调整了数据库以维持生计,这是一个最大的低悬果实来源之一。

NOW

现在

To answer your question, IF your query has a small number of distinct numeric values, you will probably not be causing issues and may in fact improve performance infinitesimally. IF however there are potentially hundreds of values and the query gets called a lot, you are going to affect the performance of your system so don't do it.

要回答您的问题,如果您的查询有少量不同的数值,您可能不会导致问题,实际上可能会无限地提高性能。但是,如果可能有数百个值,并且查询被多次调用,您将会影响系统的性能,所以不要这样做。

Yes you can increase the SQL buffer but it's always ultimately at the expense of other more critical uses for memory like caching Indexes or Data. Moral, use parametrized queries pretty religiously so you can optimize your database and use more server memory for the stuff that matters...

是的,您可以增加SQL缓冲区,但它最终总是以牺牲其他更关键的内存使用为代价,比如缓存索引或数据。道德上,要严格使用参数化查询,这样你就可以优化你的数据库,为重要的事情使用更多的服务器内存。

#8


9  

To add some info to Maciek answer:

添加一些信息到马切克的回答:

It is easy to alter the culture info of a .NET third party app by calling the main-function of the assembly by reflection:

通过反射调用程序集的主函数,很容易改变。net第三方应用程序的文化信息:

using System;
using System.Globalization;
using System.Reflection;
using System.Threading;

namespace ConsoleApplication2
{
  class Program
  {
    static void Main(string[] args)
    {
      Assembly asm = Assembly.LoadFile(@"C:\BobbysApp.exe");
      MethodInfo mi = asm.GetType("Test").GetMethod("Main");
      mi.Invoke(null, null);
      Console.ReadLine();
    }

    static Program()
    {
      InstallBobbyTablesCulture();
    }

    static void InstallBobbyTablesCulture()
    {
      CultureInfo bobby = (CultureInfo)CultureInfo.InvariantCulture.Clone();
      bobby.DateTimeFormat.ShortDatePattern = @"yyyy-MM-dd'' OR ' '=''";
      bobby.DateTimeFormat.LongTimePattern = "";
      bobby.NumberFormat.NegativeSign = "1 OR 1=1 OR 1=";
      Thread.CurrentThread.CurrentCulture = bobby;
    }
  }
}

This only works if the Main function of BobbysApp is public. If Main is not public, there might be other public functions you might call.

只有当BobbysApp的主要功能是公共的时候,它才能运行。如果Main不是公共的,那么您可能会调用其他公共函数。

#9


8  

In my opinion if you can guarantee that the parameter you working with will never contain a string it is safe but I would not do it in any case. Also, you will see a slight performance drop due to the fact that you are performing concatenation. The question I would ask you is why don't you want to use parameters?

在我看来,如果你能保证你使用的参数永远不会包含字符串,它是安全的,但无论如何我都不会这么做。此外,由于正在执行连接操作,您将看到性能略有下降。我想问你的问题是为什么不使用参数呢?

#10


4  

It is ok but never safe.. and the security always depend on the inputs, for example if the input object is TextBox, the attackers can do something tricky since the textbox can accept string, so you have to put some kind of validation/conversion to be able prevent users the wrong input. But the thing is, it is not safe. As simply as that.

这是可以的,但绝不安全。安全性总是依赖于输入,例如如果输入对象是文本框,攻击者可以做一些棘手的事情,因为文本框可以接受字符串,所以你必须进行某种验证/转换,以防止用户输入错误。但问题是,这并不安全。这么简单。

#11


-2  

No you can get an SQL injection attack that way. I have written an old article in Turkish which shows how here. Article example in PHP and MySQL but concept works same in C# and SQL Server.

不,您可以通过这种方式获得SQL注入攻击。我用土耳其语写了一篇老文章,说明了如何在这里。PHP和MySQL中的示例,但是概念在c#和SQL Server中是一样的。

Basically you attack following way. Lets consider you have a page which shows information according to integer id value. You do not parametrized this in value, like below.

基本上你是按照这种方式攻击的。让我们假设您有一个页面,该页面根据整数id值显示信息。您没有参数化这个值,如下所示。

http://localhost/sqlEnjeksiyon//instructors.aspx?id=24

Okay, I assume you are using MySQL and I attack following way.

我假设你用的是MySQL,我按这种方式攻击。

http://localhost/sqlEnjeksiyon//instructors.aspx?id=ASCII((SELECT%20DATABASE()))

Note that here injected value is not string. We are changing char value to int using ASCII function. You can accomplish same thing in SQL Server using "CAST(YourVarcharCol AS INT)".

注意,这里注入的值不是字符串。我们正在使用ASCII函数将char值更改为int。您可以使用“CAST(YourVarcharCol AS INT)”在SQL Server中完成相同的任务。

After that I use length and substring functions to find about your database name.

然后我使用length和substring函数来查找数据库名。

http://localhost/sqlEnjeksiyon//instructors.aspx?id=LEN((SELECT%20DATABASE()))

http://localhost/sqlEnjeksiyon//instructors.aspx?id=ASCII(SUBSTR(SELECT%20DATABASE(),1,1))

Then using database name, you start to get table names in database.

然后使用数据库名,开始在数据库中获取表名。

http://localhost/sqlEnjeksiyon//instructors.aspx?id=ASCII(SUBSTR((SELECT table_name FROM INFORMATION_SCHEMA.TABLES LIMIT 1),1,1))

Of course you have to automate this process, since you only get ONE character per query. But you can easily automate it. My article shows one example in watir. Using only one page and not parameterized ID value. I can learn every table name in your database. After that I can look for important tables. It will take time but it is doable.

当然,您必须自动化这个过程,因为每个查询只有一个字符。但是你可以很容易地把它自动化。我的文章展示了watir中的一个例子。只使用一个页面而不使用参数化ID值。我可以了解您数据库中的每个表名。在那之后,我可以寻找重要的桌子。这需要时间,但却是可行的。

#12


-3  

Well... one thing is sure: Security it is NOT ok, when you concatenate a string (taken by the user) with your SQL command string. It is not matter whenever the where clause refers to an Integer or to any type; injections could occur.

嗯…有一件事是肯定的:当您用SQL命令字符串连接一个字符串(由用户接收)时,安全性是不允许的。无论where子句是指整数还是任何类型,都不重要;注射可能发生。

What matters in SQL Injection is the data type of the variable that used to get the value from the user.

在SQL注入中重要的是用于从用户那里获取值的变量的数据类型。

Supposing we have an integer in the where clause and:

假设where子句和:

  1. the user-variable is a string. Then ok, it is not very easy to inject (using UNION) but it is very easy to bypass using 'OR 1=1' - like attacks...

    用户变量是一个字符串。那么,注入(使用UNION)不是很容易,但是使用'OR 1=1'之类的攻击就很容易绕过……

  2. If the user-variable is a integer. Then again we can 'test' the strength of the system by passing unusual big numbers testing for system crashes or even for a hidden buffer overflow (on the final string)... ;)

    如果用户变量是整数。然后,我们可以通过不寻常的大数字测试来测试系统崩溃,甚至是隐藏的缓冲区溢出(在最后的字符串中)……,)

Maybe the parameters to queries or (even better - imo) to Stored Procedures are not a 100% Threats safe, but they are the least required measure (or the elementary one if you prefer) to minimize them.

也许查询或(甚至更好的是——imo)存储过程的参数不是100%安全的,但是它们是最小化它们所需的最基本的度量(如果您愿意的话)。

#1


100  

I think it's safe... technically, but it's a terrible habit to get into. Do you really want to be writing queries like this?

我认为这是安全的……从技术上来说,这是一个可怕的习惯。你真的想写这样的查询吗?

var sqlCommand = new SqlCommand("SELECT * FROM People WHERE IsAlive = " + isAlive + 
" AND FirstName = @firstName");

sqlCommand.Parameters.AddWithValue("firstName", "Rob");

It also leaves you vulnerable in the situation where a type changes from an integer to a string (Think employee number which, despite its name - may contain letters).

当一个类型从一个整数变为一个字符串时(请考虑员工编号,尽管它的名称是name的),它可能包含字母。

So, we've changed the type of EmployeeNumber from int to string, but forgot to update our sql queries. Oops.

因此,我们已经将EmployeeNumber类型从int改为string,但是忘记了更新sql查询。哦。

#2


65  

When using a strongly-typed platform on a computer you control (like a web server), you can prevent code injection for queries with only bool, DateTime, or int (and other numeric) values. What is a concern are performance issues caused by forcing sql server to re-compile every query, and by preventing it from getting good statistics on what queries are run with what frequency (which hurts cache management).

当在您控制的计算机(如web服务器)上使用强类型平台时,您可以防止只使用bool、DateTime或int(和其他数值)值的查询的代码注入。令人关注的是强制sql server重新编译每个查询所导致的性能问题,并防止它获得关于哪些查询以什么频率运行的良好统计信息(这会损害缓存管理)。

Really, there's no good reason not to use query parameters for these values. It's the right way to go about this. Go ahead and hard-code values into the sql string when they really are constants, but otherwise, why not just use a parameter? It's not like it's hard.

实际上,没有理由不使用这些值的查询参数。这是正确的做法。当sql字符串实际上是常量时,继续对其进行硬编码,否则,为什么不使用参数呢?这并不难。

I also like to think long-term. What happens when today's old-and-busted strongly-typed code base gets ported via automatic translation to the new-hotness dynamic language, and you suddenly lose the type checking, but don't have all the right unit tests yet for the dynamic code?

我也喜欢长远考虑。当今天的旧式强类型代码库通过自动转换到新热点动态语言进行移植时,您突然丢失了类型检查,但还没有对动态代码进行所有正确的单元测试时,会发生什么情况?

Ultimately, I wouldn't call this a bug, per se, but I would call it a smell: something that falls just short of a bug by itself, but is a strong indication that bugs are nearby, or will be eventually. Good code avoids leaving smells, and any good static analysis tool will flag this.

最终,我不会把它本身称为bug,但我会把它称为一种气味:一种本身没有bug的东西,但它强烈地表明bug就在附近,或者最终会存在。好的代码避免留下气味,任何好的静态分析工具都会标记这一点。

I'll add that this is not, unfortunately, the kind of argument you can win straight up. It sounds like a situation where being "right" is no longer enough, and stepping on your co-workers toes to fix this issue on your own isn't likely to promote good team dynamics; it could ultimately hurt more than it helps. A better approach in this case may be to promote the use of a static analysis tool. That would give legitimacy and credibility to efforts aimed and going back and fixing existing code.

我要补充一点,不幸的是,这不是那种你可以直接赢得的论点。这听起来似乎是一种“正确”已经不再足够的情况,你自己去解决这个问题也不太可能促进良好的团队活力;它最终可能造成的伤害比帮助更大。在这种情况下,更好的方法可能是促进使用静态分析工具。这将使旨在恢复和修正现有代码的努力具有合法性和可信性。

#3


53  

In some cases, it IS possible to perform SQL injection attack with non-parametrized (concatenated) variables other than string values - see this article by Jon: http://codeblog.jonskeet.uk/2014/08/08/the-bobbytables-culture/ .

在某些情况下,可以使用非参数化的(连接的)变量来执行SQL注入攻击,而不是字符串值——请参见Jon: http://codeblog.jonskeet。英国/ 2014/08/08 / the-bobbytables-culture /。

Thing is that when ToString is called, some custom culture provider can transform a non-string parameter into its string representation which injects some SQL into the query.

当调用ToString时,一些自定义区域性提供程序可以将非字符串参数转换为字符串表示形式,从而向查询注入一些SQL。

#4


47  

This is not safe even for non-string types. Always use parameters. Period.

即使是非字符串类型也不安全。总是使用参数。时期。

Consider following code example:

考虑下面的代码示例:

var utcNow = DateTime.UtcNow;
var sqlCommand = new SqlCommand("SELECT * FROM People WHERE created_on <= '" + utcNow + "'");

At the first glance code looks safe, but everything changes if you make some changes in Windows Regional Settings and add injection in short date format:

乍一看,代码看起来很安全,但如果你对Windows区域设置做了一些更改,并以短日期格式添加注入,一切都会改变:

当参数不是字符串时,不参数化SQL查询是否安全?

Now resulting command text looks like this:

现在生成的命令文本如下所示:

SELECT * FROM People WHERE created_on <= '26.09.2015' OR '1'<>' 21:21:43'

The same can be done for int type as user can define custom negative sign which can be easily changed into SQL injection.

对于int类型也可以这样做,因为用户可以定义自定义的负号,可以很容易地将其转换为SQL注入。

One could argue that invariant culture should be used instead of current culture, but I have seen string concatenations like this so many times and it is quite easy to miss when concatenating strings with objects using +.

有人可能会说应该使用不变文化而不是当前文化,但是我已经见过很多次像这样的字符串连接,当使用+连接字符串时很容易被忽略。

#5


24  

"SELECT * FROM Table1 WHERE Id=" + intVariable.ToString()

"SELECT * FROM Table1, Id=" + intVariable.ToString()


Security
It is OK.
Attackers can not inject anything in your typed int variable.

安全就可以了。攻击者不能在类型int变量中插入任何内容。

Performance
Not OK.

性能不好。

It's better to use parameters, so the query will be compiled once and cached for next usage. Next time even with different parameter values, query is cached and doesn't need to compile in database server.

最好使用参数,因此查询将被编译一次并缓存以备下次使用。下次即使使用不同的参数值,查询也会被缓存,不需要在数据库服务器中编译。

Coding Style
Bad practice.

编码风格不好的实践。

  • Parameters are more readable
  • 参数更具可读性
  • Maybe it makes you get used to queries without parameters, then maybe you made a mistake once and use a string value this way and then you probably should say goodbye to your data. Bad habit!
  • 也许它让你习惯了没有参数的查询,然后也许你犯了一个错误,用这种方式使用一个字符串值,然后你可能应该对你的数据说再见。坏习惯!


"SELECT * FROM Product WHERE Id=" + TextBox1.Text

“从Id=”+ TextBox1.Text的产品中选择*


Although it is not your question, but maybe useful for future readers:

虽然这不是你的问题,但可能对未来的读者有用:

Security
Disaster!
Even when the Id field is integer, your query may be subject to SQL Injection. Suppose you have a query in your application "SELECT * FROM Table1 WHERE Id=" + TextBox1.Text, An attacker can insert into text box 1; DELETE Table1 and the query will be:

安全灾难!即使Id字段是整数,您的查询也可能受到SQL注入的影响。假设您的应用程序“SELECT * FROM Table1”中有一个查询,其中Id=" + TextBox1。攻击者可以在文本框1中插入;删除表1,查询为:

"SELECT * FROM Table1 WHERE Id=1; DELETE Table1"

If you don't want to use parametrized query here, you should use typed values:

如果您不想在这里使用参数化查询,您应该使用类型化值:

string.Format("SELECT * FROM Table1 WHERE Id={0}", int.Parse(TextBox1.Text))


Your Question

你的问题


My question arose because a coworker wrote a bunch of queries concatenating integer values, and I was wondering whether it was a waste of my time to go through and fix all of them.

我的问题出现是因为一位同事写了一串连接整数值的查询,我想知道我是否在浪费时间去检查并修复它们。

I think changing those codes is not waste of time. Indeed change is Recommended!

我认为改变这些代码不是浪费时间。事实上改变推荐!

if your coworker uses int variables, it has no security risk , But I think changing those codes is not waste of time and indeed changing those codes is recommended. It makes code more readable, more maintainable and makes execution faster.

如果您的同事使用int变量,则没有安全风险,但是我认为更改这些代码并不浪费时间,确实建议更改这些代码。它使代码更具可读性,更易于维护,并且使执行速度更快。

#6


19  

There are actually two questions in one. And question from the title has very little to do with concerns expressed by the OP in the comments afterwards.

实际上有两个问题。题目中的问题与OP在后面的评论中表达的担忧关系不大。

Although I realize that for the OP it is their particular case that matters, for the readers coming from Google, it is important to answer to the more general question, that can be phrased as "is concatenation as safe as prepared statements if I made sure that every literal I am concatenating is safe?". So, I would like to concentrate on this latter one. And the answer is

虽然我知道OP才是他们的具体情况,为读者来自谷歌,重要的是要回答到更一般的问题,可以以“是连接一样安全准备好的语句如果我确信每一个文字连接是安全的?”。因此,我想把注意力集中在后一个问题上。答案是

Definitely NO.

The explanation is not that direct as most readers would like, but I'll try my best.

这种解释并不像大多数读者所想的那样直接,但我会尽我最大的努力。

I have been pondering on the matter for a while, resulting in the article (though based on the PHP environment) where I tried to sum everything up. It occurred to me that the question of protection from SQL injection is often eludes toward some related but narrower topics, like string escaping, type casting and such. Although some of the measures can be considered safe when taken by themselves, there is no system, nor a simple rule to follow. Which makes it very slippery ground, putting too much on the developer's attention and experience.

我已经对这个问题思考了一段时间,并在本文中(尽管基于PHP环境)尝试对所有内容进行总结。我突然想到,保护不受SQL注入影响的问题常常回避一些相关但较窄的主题,如字符串转义、类型转换等。虽然有些措施本身可以被认为是安全的,但没有制度,也没有简单的规则可以遵循。这使它变得非常滑,给开发人员太多的注意力和经验。

The question of SQL injection cannot be simplified to a matter of some particular syntax issue. It is wider than average developer used to think. It's a methodological question as well. It is not only "Which particular formatting we have to apply", but "How it have to be done" as well.

SQL注入的问题不能简化为某些特定语法问题。它比一般的开发人员想象的要宽。这也是一个方法论的问题。它不仅是“我们必须应用的特定格式”,还包括“如何完成”。

(From this point of view, an article from Jon Skeet cited in the other answer is doing rather bad than good, as it is again nitpicking on some edge case, concentrating on a particular syntax issue and failing to address the problem at whole.)

(从这个角度来看,另一个答案中引用的Jon Skeet的一篇文章做得相当糟糕,而不是很好,因为它再次挑剔某些edge案例,专注于特定的语法问题,而未能完全解决问题。)

When you're trying to address the question of protection not as whole but as a set of different syntax issues, you're facing multitude of problems.

当您试图解决保护问题而不是作为一组不同的语法问题时,您将面临大量的问题。

  • the list of possible formatting choices is really huge. Means one can easily overlook some. Or confuse them (by using string escaping for identifier for example).
  • 可能的格式选择列表非常巨大。意思是一个人可以很容易地忽略一些。或者混淆它们(例如使用字符串转义作为标识符)。
  • Concatenation means that all protection measures have to be done by the programmer, not program. This issue alone leads to several consequences:
    • such a formatting is manual. Manual means extremely error prone. One could simply forget to apply.
    • 这种格式是手动的。手动意味着极容易出错。你可以简单地忘记申请。
    • moreover, there is a temptation to move formatting procedures into some centralized function, messing things even more, and spoiling data that is not going to database.
    • 此外,还有一种诱惑,即将格式化过程转移到某个集中函数中,从而使事情变得更混乱,并破坏不进入数据库的数据。
  • 连接意味着所有的保护措施必须由程序员完成,而不是程序。仅这个问题就会导致几个后果:这种格式是手动的。手动意味着极容易出错。人们可能会忘记申请。此外,还有一种诱惑,即将格式化过程转移到某个集中函数中,从而使事情变得更混乱,并破坏不进入数据库的数据。
  • when more than one developers involved, problems multiply by a factor of ten.
  • 当涉及多个开发人员时,问题会乘以10倍。
  • when concatenation is used, one cannot tell a potentially dangerous query at glance: they all potentially dangerous!
  • 当使用连接时,不能一眼就看出一个潜在危险的查询:它们都有潜在危险!

Unlike that mess, prepared statements are indeed The Holy Grail:

与那种混乱不同,事先准备好的声明确实是圣杯:

  • it can be expressed in the form of one simple rule that is easy to follow.
  • 它可以用一个简单的规则来表示,这个规则很容易遵循。
  • it is essentially undetacheable measure, means the developer cannot interfere, and, willingly or unwillingly, spoil the process.
  • 它本质上是不可分离的度量,这意味着开发人员不能进行干预,并且愿意或不情愿地破坏这个过程。
  • protection from injection is really only a side effect of the prepared statements, which real purpose is to produce syntactically correct statement. And a syntactically correct statement is 100% injection proof. Yet we need our syntax to be correct despite of any injection possibility.
  • 对注入的保护实际上只是准备语句的副作用,其真正目的是生成语法正确的语句。语法正确的语句是100%注射证明。然而,我们需要我们的语法是正确的,尽管有任何注入的可能性。
  • if used all the way around, it protects the application regardless of the developer's experience. Say, there is a thing called second order injection. And a very strong delusion that reads "in order to protect, Escape All User Supplied Input". Combined together, they lead to injection, if a developer takes the liberty to decide, what needs to be protected and what not.
  • 如果一直使用,不管开发人员的经验如何,它都会保护应用程序。有一种东西叫做二阶注入。一种非常强烈的错觉,写着“为了保护、逃避所有用户提供的输入”。结合在一起,它们将导致注入,如果开发人员可以*地决定,哪些需要保护,哪些不需要。

(Thinking further, I discovered that current set of placeholders is not enough for the real life needs and have to be extended, both for the complex data structures, like arrays, and even SQL keywords or identifiers, which have to be sometimes added to the query dynamically too, but a developer is left unarmed for such a case, and forced to fall back to string concatenation but that's a matter of another question).

(进一步思考,我发现现在的占位符是不够现实生活需求和扩展,对复杂的数据结构,数组,甚至SQL关键字或标识符,有时需要动态添加到查询中,但开发人员离开手无寸铁的这种情况下,和*回到字符串连接,但这是另一个问题的问题)。

Interestingly, this question's controversy is provoked by the very controversial nature of Stack Overflow. The site's idea is to make use of particular questions from users who ask directly to achieve the goal of having a database of general purpose answers suitable for users who come from search. The idea is not bad per se, but it fails in a situation like this: when a user asks a very narrow question, particularly to get an argument in a dispute with a colleague (or to decide if it worth to refactor the code). While most of experienced participants are trying to write an answer, keeping in mind the mission of Stack Overflow at whole, making their answer good for as many readers as possible, not the OP only.

有趣的是,这个问题的争议是由Stack Overflow的极具争议性引起的。该网站的想法是利用用户直接提出的特定问题,以实现拥有适合搜索用户的通用答案数据库的目标。这个想法本身并不坏,但在这种情况下它就失败了:当用户问一个非常狭窄的问题时,尤其是在与同事发生争执时(或者决定是否值得重构代码)。当大多数有经验的参与者都在尝试编写一个答案时,请记住Stack Overflow的使命,使他们的答案对尽可能多的读者有效,而不仅仅是OP。

#7


16  

Let's not just think about security or type-safe considerations.

让我们不只是考虑安全性或类型安全的考虑。

The reason you use parametrized queries is to improve performance at the database level. From a database perspective, a parametrized query is one query in the SQL buffer (to use Oracle's terminology although I imagine all databases have a similar concept internally). So, the database can hold a certain amount of queries in memory, prepared and ready to execute. These queries do not need to be parsed and will be quicker. Frequently run queries will usually be in the buffer and will not need parsing every time they are used.

使用参数化查询的原因是为了提高数据库级别的性能。从数据库的角度来看,参数化查询是SQL缓冲区中的一个查询(使用Oracle的术语,尽管我认为所有数据库内部都有类似的概念)。因此,数据库可以在内存中保存一定数量的查询,并准备好执行。这些查询不需要解析,而且会更快。经常运行的查询通常位于缓冲区中,每次使用它们时都不需要解析。

UNLESS

除非

Somebody doesn't use parametrized queries. In this case, the buffer gets continually flushed through by a stream of nearly identical queries each of which needs to be parsed and run by the database engine and performance suffers all-round as even frequently run queries end up being re-parsed many times a day. I have tuned databases for a living and this has been one of the biggest sources of low-hanging fruit.

有些人不使用参数化查询。在这种情况下,缓冲区会被几乎相同的查询流不断刷新,每个查询都需要被解析并由数据库引擎运行,性能会受到全面影响,因为即使是频繁运行的查询,也会被每天多次重新解析。我已经调整了数据库以维持生计,这是一个最大的低悬果实来源之一。

NOW

现在

To answer your question, IF your query has a small number of distinct numeric values, you will probably not be causing issues and may in fact improve performance infinitesimally. IF however there are potentially hundreds of values and the query gets called a lot, you are going to affect the performance of your system so don't do it.

要回答您的问题,如果您的查询有少量不同的数值,您可能不会导致问题,实际上可能会无限地提高性能。但是,如果可能有数百个值,并且查询被多次调用,您将会影响系统的性能,所以不要这样做。

Yes you can increase the SQL buffer but it's always ultimately at the expense of other more critical uses for memory like caching Indexes or Data. Moral, use parametrized queries pretty religiously so you can optimize your database and use more server memory for the stuff that matters...

是的,您可以增加SQL缓冲区,但它最终总是以牺牲其他更关键的内存使用为代价,比如缓存索引或数据。道德上,要严格使用参数化查询,这样你就可以优化你的数据库,为重要的事情使用更多的服务器内存。

#8


9  

To add some info to Maciek answer:

添加一些信息到马切克的回答:

It is easy to alter the culture info of a .NET third party app by calling the main-function of the assembly by reflection:

通过反射调用程序集的主函数,很容易改变。net第三方应用程序的文化信息:

using System;
using System.Globalization;
using System.Reflection;
using System.Threading;

namespace ConsoleApplication2
{
  class Program
  {
    static void Main(string[] args)
    {
      Assembly asm = Assembly.LoadFile(@"C:\BobbysApp.exe");
      MethodInfo mi = asm.GetType("Test").GetMethod("Main");
      mi.Invoke(null, null);
      Console.ReadLine();
    }

    static Program()
    {
      InstallBobbyTablesCulture();
    }

    static void InstallBobbyTablesCulture()
    {
      CultureInfo bobby = (CultureInfo)CultureInfo.InvariantCulture.Clone();
      bobby.DateTimeFormat.ShortDatePattern = @"yyyy-MM-dd'' OR ' '=''";
      bobby.DateTimeFormat.LongTimePattern = "";
      bobby.NumberFormat.NegativeSign = "1 OR 1=1 OR 1=";
      Thread.CurrentThread.CurrentCulture = bobby;
    }
  }
}

This only works if the Main function of BobbysApp is public. If Main is not public, there might be other public functions you might call.

只有当BobbysApp的主要功能是公共的时候,它才能运行。如果Main不是公共的,那么您可能会调用其他公共函数。

#9


8  

In my opinion if you can guarantee that the parameter you working with will never contain a string it is safe but I would not do it in any case. Also, you will see a slight performance drop due to the fact that you are performing concatenation. The question I would ask you is why don't you want to use parameters?

在我看来,如果你能保证你使用的参数永远不会包含字符串,它是安全的,但无论如何我都不会这么做。此外,由于正在执行连接操作,您将看到性能略有下降。我想问你的问题是为什么不使用参数呢?

#10


4  

It is ok but never safe.. and the security always depend on the inputs, for example if the input object is TextBox, the attackers can do something tricky since the textbox can accept string, so you have to put some kind of validation/conversion to be able prevent users the wrong input. But the thing is, it is not safe. As simply as that.

这是可以的,但绝不安全。安全性总是依赖于输入,例如如果输入对象是文本框,攻击者可以做一些棘手的事情,因为文本框可以接受字符串,所以你必须进行某种验证/转换,以防止用户输入错误。但问题是,这并不安全。这么简单。

#11


-2  

No you can get an SQL injection attack that way. I have written an old article in Turkish which shows how here. Article example in PHP and MySQL but concept works same in C# and SQL Server.

不,您可以通过这种方式获得SQL注入攻击。我用土耳其语写了一篇老文章,说明了如何在这里。PHP和MySQL中的示例,但是概念在c#和SQL Server中是一样的。

Basically you attack following way. Lets consider you have a page which shows information according to integer id value. You do not parametrized this in value, like below.

基本上你是按照这种方式攻击的。让我们假设您有一个页面,该页面根据整数id值显示信息。您没有参数化这个值,如下所示。

http://localhost/sqlEnjeksiyon//instructors.aspx?id=24

Okay, I assume you are using MySQL and I attack following way.

我假设你用的是MySQL,我按这种方式攻击。

http://localhost/sqlEnjeksiyon//instructors.aspx?id=ASCII((SELECT%20DATABASE()))

Note that here injected value is not string. We are changing char value to int using ASCII function. You can accomplish same thing in SQL Server using "CAST(YourVarcharCol AS INT)".

注意,这里注入的值不是字符串。我们正在使用ASCII函数将char值更改为int。您可以使用“CAST(YourVarcharCol AS INT)”在SQL Server中完成相同的任务。

After that I use length and substring functions to find about your database name.

然后我使用length和substring函数来查找数据库名。

http://localhost/sqlEnjeksiyon//instructors.aspx?id=LEN((SELECT%20DATABASE()))

http://localhost/sqlEnjeksiyon//instructors.aspx?id=ASCII(SUBSTR(SELECT%20DATABASE(),1,1))

Then using database name, you start to get table names in database.

然后使用数据库名,开始在数据库中获取表名。

http://localhost/sqlEnjeksiyon//instructors.aspx?id=ASCII(SUBSTR((SELECT table_name FROM INFORMATION_SCHEMA.TABLES LIMIT 1),1,1))

Of course you have to automate this process, since you only get ONE character per query. But you can easily automate it. My article shows one example in watir. Using only one page and not parameterized ID value. I can learn every table name in your database. After that I can look for important tables. It will take time but it is doable.

当然,您必须自动化这个过程,因为每个查询只有一个字符。但是你可以很容易地把它自动化。我的文章展示了watir中的一个例子。只使用一个页面而不使用参数化ID值。我可以了解您数据库中的每个表名。在那之后,我可以寻找重要的桌子。这需要时间,但却是可行的。

#12


-3  

Well... one thing is sure: Security it is NOT ok, when you concatenate a string (taken by the user) with your SQL command string. It is not matter whenever the where clause refers to an Integer or to any type; injections could occur.

嗯…有一件事是肯定的:当您用SQL命令字符串连接一个字符串(由用户接收)时,安全性是不允许的。无论where子句是指整数还是任何类型,都不重要;注射可能发生。

What matters in SQL Injection is the data type of the variable that used to get the value from the user.

在SQL注入中重要的是用于从用户那里获取值的变量的数据类型。

Supposing we have an integer in the where clause and:

假设where子句和:

  1. the user-variable is a string. Then ok, it is not very easy to inject (using UNION) but it is very easy to bypass using 'OR 1=1' - like attacks...

    用户变量是一个字符串。那么,注入(使用UNION)不是很容易,但是使用'OR 1=1'之类的攻击就很容易绕过……

  2. If the user-variable is a integer. Then again we can 'test' the strength of the system by passing unusual big numbers testing for system crashes or even for a hidden buffer overflow (on the final string)... ;)

    如果用户变量是整数。然后,我们可以通过不寻常的大数字测试来测试系统崩溃,甚至是隐藏的缓冲区溢出(在最后的字符串中)……,)

Maybe the parameters to queries or (even better - imo) to Stored Procedures are not a 100% Threats safe, but they are the least required measure (or the elementary one if you prefer) to minimize them.

也许查询或(甚至更好的是——imo)存储过程的参数不是100%安全的,但是它们是最小化它们所需的最基本的度量(如果您愿意的话)。