这个SQL注入如何工作? 需要说明

我正在学习RoR /数据库,这个话题让我特别困惑。 在Agile Development with Rails 4一书中,他们举例说明了一个名为Dave的条目的所有订单列表:

pos = Order.where("name = 'Dave' and pay_type = 'po") 

这本书继续说你永远不想做这样的事情:

 name = params[:name] pos = Order.where("name = '#{name}'and pay_type = 'po'") 

相反,你应该这样做:

 name = params[:name] pos = Order.where(["name = ? and pay_type = 'po'",name]) 

我理解,SQL注入是一个概念,但有一些细节让我感到困惑。 对于初学者来说,SQL注入到底是如何工作的一种语法。

我知道,如果你插入一个像第一个例子那样的外部表单参数,有人可以删除一个表/数据库但是如何?

让我们说你有这个:

 name = params[:name] #DROP DATABASE database_name pos = Order.where("name = '#{DROP DATABASE database_name}'and pay_type = 'po'") 

这是SQL注入的工作原理吗? SQL是一种语法,数据库中应该没有“name = DROP DATABASE database_name”的字段,这不会返回错误而不是丢弃数据库吗?

此外,问号版本将如何防止这种情况发生。 再说一次,假设您有这种情况。

 name = params[:name] #DROP DATABASE database_name pos = Order.where(["name = ? and pay_type = 'po'", DROP DATABASE database_name]) 

这不会用DROP DATABASE database_name语法替换问号,那么我们不会遇到第一个例子中的问题吗? 这究竟是如何保护应用程序免受SQL攻击? 我在http://hub.tutsplus.com/上搜索了一些教程并在Google上搜索过,但我没有得到它背后的概念。 有帮助吗?

最简单的解释我可以给出SQL注入的内容:

这可能会使SQL查询如下所示:

 SELECT * FROM Order WHERE name = 'Dan' AND pay_type = 'po' 

现在一个不错的用户会像上面那样提供Dan这个名字。

但是一个邪恶的用户(让我们称他为Bobby),将提供名称: Bobby Tables'; DROP DATABASE master; -- Bobby Tables'; DROP DATABASE master; --

这会创建一个类似的查询:

 SELECT * FROM Order WHERE name = 'Bobby Tables'; DROP DATABASE master; --' AND pay_type = 'po' 

这有效地执行了两个查询:

 SELECT * FROM Order WHERE name = 'Bobby Tables'; DROP DATABASE master; 

现在数据库已经消失了。 当他们将私人信息从数据库中取出时(如用户名/密码或信用卡信息),会造成更严重的损害


至于为什么问号神奇地保护你:

使用RoR中的问号,使用称为参数化的模式。 在参数化SQL查询时,您可以使用它来阻止任何人输入成功的SQL注入。 在任何地方使用问号,它都被参数替换。 然后通过转义任何引用将该参数安全地设置为查询顶部的值。

如果您现在提供Dan名称:

 Order.where(["name = ? and pay_type = 'po'", params[:name]) 

查询看起来像:( RoR可能在内部略微不同地参数化,但效果是相同的)

 DECLARE @p0 nvarchar(4000) = N'po', @p1 nvarchar(4000) = N'Dan'; SELECT [t0].[ID], [t0].[name], [t0].[pay_type] FROM Order AS [t0] WHERE ([t0].[name] = @p1) AND ([t0].[pay_type] = @p1) 

现在如果邪恶的Bobby带着他的名字:“Bobby Tables”; DROP DATABASE主; –

if会参数化(和转义引用)查询,如:

 DECLARE @p0 nvarchar(4000) = N'po', @p1 nvarchar(4000) = N'Bobby Tables''; DROP DATABASE master; --'; SELECT [t0].[ID], [t0].[name], [t0].[pay_type] FROM Order AS [t0] WHERE ([t0].[name] = @p1) AND ([t0].[pay_type] = @p1) 

现在这是一个非常安全的查询

希望能帮助您理解

它与代码解释器的工作方式有关。

在第一个示例中,参数只是作为文本插入,然后处理整个命令。 因此,问题。

在第二个示例中,首先解释命令,然后插入参数。 (IE,它解释“do name where name = [some parameter]”,然后在它之后添加参数。)所以你得到的将是一个非常奇怪的平等,其中name =“); drop table blah ;” 除非你的数据中有一些奇怪的名字,否则这当然不会奏效。

注意,注入必须实际正确地结束命令并启动一个新命令 – 否则它只会导致错误。

这似乎是一个小小的狡辩,但如果你没有做到这一点,它会产生巨大的影响。 纪律极为重要。 如果你忘记了这一点, 你会度过糟糕的一天,因为一旦发现漏洞,人们可以做些非常讨厌的事情 。

每天都有另一个例子,说明一开始就没有认真对待一个小小的错误会导致严重的问题。 今天的例子:由于单个参数没有被转义, 4chan被黑了。 这就是全部。 一个错误。

尽可能使用SQL占位符作为值。 不要走快捷方式。 风险太高了。

ActiveRecord有许多方法来编写不涉及直接操作查询的查询:

 Order.where(name: 'name', pay_type: 'po') 

尽可能使用这些。 如果您遇到限制,请尽可能安全地进行:

 Order.where([ 'name LIKE ?', "%#{name}" ]) 

您还可以更直接地使用转义function:

 Order.sanitize(name) 

如果您必须在查询中引入一些任意文本,请确保它已经过彻底validation。 始终列出一个非常狭窄的已接受参数列表。 不要错误地添加一些排除规则,这样就会忘记忘记规则。 它的音质要比太宽松要严格得多。

使用Rails,你的基础非常好。 不要搞砸了。 避免大量混乱的一种方法是使用单引号字符串,因此插值是禁止的:

 Order.where('name="#{name}"') # Won't work, isn't interpolated. 

如果养成这种习惯,那么你应该非常犹豫是否要切换到内插字符串。

如果您掌握了所有这些,那么您需要记住,在HTML空间中需要了解完全相同的模式,您需要了解需要相同级别规则的XSS和HTML注入问题。 在现代Rails中,所有用户数据都在视图中自动进行HTML转义,但这并不总是有助于嵌入式JavaScript。 每当您有疑问时,询问您所做的事情是否安全无法确定。

关于这方面的内容很多,所以我不打算讨论它,但是为了回答你的问题,黑客可以发出一个GO然后删除数据库OR; DROP DATABASE等。作为Heartbleed已经告诉我们,如果你不知道它会更具破坏性,他们可以做一些像“99;从主人那里选择*”来获取敏感信息。 为了保护,您最常被引导使用存储过程WITH参数来让DBMSvalidation并且永远不会将事物串在一起作为动态SQL,除非您真的必须这样做。 它很危险,性能几乎总是受到负面影响。