2016-12-31 80 views
16

我已经阅读了一段时间,现在在各种SO线程,指南等,但所有的答案是相互矛盾和矛盾的。Rails 5 SQL注入

看来有很多类似的方法,并且很多答案都表示使用不同的方法。

  • 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的查询,但允许我插入我自己的参数来自于危险的用户输入。

,因为我在做复杂的纬度/长计算,聚合函数,子查询复杂等

到目前为止,我已经试过2点的方法,我不能在这些少数情况下使用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。但根据this SO post它只是简单地用单引号包装的东西是不安全的......所以我不知道。

方法2

我不知道这是否是一样安全,但它似乎用实际PG准备功能(我假设是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能力的地方,我需要一个很好的lib,我可以将其包含在我所知道的所有项目中,这些项目我都是完全安全的。

回答

9

使用quote是安全的。我在the page you linked to上读到了答案,我没有看到有人说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的评论,但你应该可能你SE quote,它试图弄清楚的数据类型,并做适当的事)

顺便说一句,这里是一个类似的问题,以你的,从我在2014年的答案,而一些替代太:How to execute a raw update sql with dynamic binding in rails

+0

我明白了 - 这很有道理。那么'sanitize'和'sanitize_sql_for_conditions'之间的主要区别是什么?另一个我忘了在我原来的问题中包含的部分是,'method sanitize_sql_for_conditions'的文档提到:*将它们清理成** WHERE **子句的有效SQL片段*。 'sanitize'的文档say *用于在对象用于SQL ** SELECT **语句*之前对对象进行清理。这是否意味着它们与情境有关,并且一种方法不能在SQL语句中的任何地方使用? (在SELECT,WHERE,GROUP BY等)。或者我可以使用'sanitize'无论位置? – Tallboy

+2

它看起来像'sanitize_ *'方法都受到保护,所以我不认为你打算使用它们。我一直都把'quote'理解为这种事情的主要公共方法。事实上,简单的'sanitize'方法只是调用'quote'(正如你所说的)。看看代码,看起来像其他'sanitize_ *'方法真的是用于桥接Railsy数据结构(例如'{name:“foo”,email:“[email protected]”}')和'quote' 。他们为每个值调用'quote'。 '* for_conditions' vs'* for_assignment'似乎主要是关于使用','vs'AND'。 –

+0

感谢您的帮助!很好的答案。 – Tallboy