2016-12-31 81 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