基于参数的联接不易受到sql注入的影响



我想用基于参数的联接进行非标准查询。

例如,我有两个表:a_examplesb_examplesield_xfield_y。当两个表在field_x(或field_y(上具有相同值时,我希望联接行。

示例查询如下所示:

AExample.joins('INNER JOIN b_examples ON b_examples.field_x = a_examples.field_x')

当我有基于参数的字段名称时,就会出现问题。例如,我有一个变量field_name,并希望将其用于查询。我可以这样做:

AExample.joins("INNER JOIN b_examples ON b_examples.#{field_name} = a_examples.#{field_name}")

此查询有效,但易受sql注入的影响。

对于where子句,我们有特殊的语法?以避免sql注入,但对于联接没有任何这样的东西。如何确保此查询的安全?

不要尝试这样做:(解释如下(

您可以使用ActiveRecord::Sanitization模块,并在ActiveRecord模型中编写如下内容:

AExample.joins("INNER JOIN b_examples ON b_examples.#{sanitize_sql(field_name)} = a_examples.#{sanitize_sql(field_name)}")

或者,您可以将模块包括在其他地方并在那里使用(例如,您的控制器(。

请使用此选项:(包含在另一个答案中(

AExample.joins("INNER JOIN b_examples ON b_examples.#{ActiveRecord::Base.connection.quote_column_name(field_name)} = a_examples.#{ActiveRecord::Base.connection.quote_column_name(field_name)}")

如果找不到该列,它将引发错误,从而阻止恶意代码进入您的查询。

然而,我不会在我的应用程序中这样做,因为它看起来很可疑,其他程序员可能不了解发生了什么,它可能实施错误,应该包括可靠的测试,它可能有错误等等。在你的问题中,你只需要构建两个不同的查询,根据这些信息,我会写一些类似的东西:

case dynamic_field
when 'field_x'
AExample.joins('INNER JOIN b_examples ON b_examples.field_x = a_examples.field_x')
when 'field_y'
AExample.joins('INNER JOIN b_examples ON b_examples.field_y = a_examples.field_y')
else
raise "Some suspicious parameter was sent!: #{dynamic_field}"
end

或者甚至在你的模型上写范围,避免这些代码四处传播。

对于这种性质的问题,如加密问题,请尝试找到解决方法,并尽可能避免实现自己的解决方案。

编辑:

方法sanitize_sql旨在净化WHERE子句(ActiveRecord::Sanitization(:的条件

接受SQL条件的数组或字符串,并将它们清除为WHERE子句的有效SQL片段。

当您尝试为INNER JOINON子句进行清理时,这不是一个选项。

注意,ActiveRecord::Sanitization模块只有WHERE、SET、ORDER和LIKE子句的选项。我找不到列名、INNER JOIN或ON子句的清理方法。也许这是一个有用的函数性,应该在下一个版本的Rails上添加。

sanitize_sql与字符串一起使用会使其几乎未经过滤,因此如果field_name变量包含以下恶意代码:

"field_x = a_examples.field_x; DROP TABLE a_examples; --"

它将包含在您的查询中,不会引发任何错误

这种解决方案是不安全的,因此我们应该避免编写这种性质的代码。也许你发现Arel或其他宝石有帮助,但我强烈建议不要这样做。

编辑2:

添加了用于转义列名的工作解决方案。如果输入了恶意代码,则会引发错误,因为找不到具有该名称的列。

您可以使用ActiveRecord::Base.connection.quote(string)甚至.quote_column对参数进行消毒

最新更新