Rails 5 SQL注入

我已经在一段时间内阅读了关于各种SO线程,指南等的内容……但所有答案都是相互矛盾和矛盾的。

似乎有许多类似的方法,很多答案都说使用不同的方法。

  • sanitize
  • sanitize_conditions
  • sanitize_sql
  • sanitize_sql_array
  • sanitize_sql_for_assignment
  • sanitize_sql_for_conditions
  • sanitize_sql_hash
  • sanitize_sql_hash_for_assignment
  • sanitize_sql_hash_for_conditions
  • sanitize_sql_like

我正在尝试编写一个“原始查询”适配器,它允许我运行原始的Postgres查询,但允许我插入来自危险用户输入的我自己的参数。

我不能在这几个实例中使用AR,因为我正在进行复杂的纬度/经度计算,聚合函数,复杂子查询等。

到目前为止,我尝试了两种方法:

方法1

对于这种方法,我不知道sanitize是否是上述的最佳选择,或者它是否适用于100%的情况……(我只使用Postgres)

 class RawQuery def exec(prepared, *params) prepared = query.dup params.flatten.each_with_index do |p, i| prepared.gsub!("$#{i + 1}", ActiveRecord::Base.sanitize(p)) end ActiveRecord::Base.connection.exec_query(prepared) end end 

琐碎的用法示例(当然通常不会这么简单,或者我只会使用AR):

RawQuery.new.exec('SELECT * FROM users WHERE name = $1', params[:name])

此外,似乎sanitize代表quote 。 但根据这篇SOpost说它只是用单引号包装东西是不安全的…所以我不知道。

方法2

我不确定这是否同样安全,但它似乎使用了一个实际的PG准备function(我认为它是100%安全的)。 唯一的问题是rails不会将其打印到控制台,也不包括SQL执行时间(这会破坏我的分析工具)。

 class RawQuery def prepare(query, *params) name = "raw_query_#{SecureRandom.uuid.gsub('-', '')}" connection = ActiveRecord::Base.connection.raw_connection connection.prepare(name, query) connection.exec_prepared(name, params) end end 

使用方式相同:

RawQuery.new.prepare('SELECT * FROM users WHERE name = $1', params[:name])


一种方法比另一种更安全吗? 100%安全吗?

我的应用程序总是远远超出了Rails的SQL能力范围,我需要一个好的库,我可以包含在我认为完全安全的所有项目中。

使用quote是安全的。 我在你链接的页面上阅读了答案,我没有看到有人说这quote是不安全的。 我看到你关于使用“引号”的问题。 是的,如果您只是在字符串周围加上引号,那就是不安全,例如:

 q = "SELECT * FROM users where email = '#{params[:email]}'" 

但是使用quote (方法)很好:

 q = "SELECT * FROM users where email = #{connection.quote(params[:email])}" 

你可以在控制台中玩游戏并尽力打破它,但我认为你不能:

 2.3.3 :003 > ActiveRecord::Base.connection.quote("f''oo") => "'f''''oo'" 

如果你成功了,我肯定Rails团队想知道(私下)! 但正如您所看到的, quote方法不仅仅是在开头和结尾都贴上引号。

此外,由于您说您正在寻找权威引文,因此源代码本身中的注释表明引用用户输入是这些函数的预期目的:

https://github.com/rails/rails/blob/2471e6391dfe71cfbb8621bdf573729d961d3209/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb#L6-L13

 # Quotes the column value to help prevent # {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection]. def quote(value) 

https://github.com/rails/rails/blob/0f1d0b1b5254e3678abaabbebb3362a100c10262/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb#L17-L20

 # Quotes strings for use in SQL input. def quote_string(s) #:nodoc: 

(注意我为评论显示quote_string ,但你应该使用quote ,它试图找出数据类型并做一些合适的事情。)

顺便说一句,这里有一个与你类似的问题,2014年我的回答,以及一些替代方案: 如何在rails中执行带有动态绑定的原始更新sql