web-dev-qa-db-ja.com

Rails 5SQLインジェクション

これについては、さまざまな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クエリを実行できる「生のクエリ」アダプタを作成しようとしていますが、危険なユーザー入力から取得した独自のパラメータを挿入できます。

複雑なlat/long計算、集計関数、複雑なサブクエリなどを実行しているため、これらのいくつかのインスタンスではARを使用できません。

これまでに2つのアプローチを試しました。

方法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])

さらに、sanitizequote に委任しているようです。しかし this SO post によると、単純に一重引用符で囲むのは安全ではないということです...だから私にはわかりません。

方法2

これが同じくらい安全かどうかはわかりませんが、実際のP​​Gで準備された関数を使用しているようです(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に関して機能するものをはるかに超えて拡張されており、完全に安全であることがわかっているすべてのプロジェクトに含めることができる優れたライブラリが必要です。

16
Tallboy

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-L1

# 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-L2

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

(コメントにはquote_stringを表示していますが、おそらくquoteを使用する必要があります。これは、データ型を把握して適切な処理を実行しようとします。)

ちなみに、これはあなたと同様の質問で、2014年に私からの回答があり、いくつかの選択肢もあります: Railsで動的バインディングを使用して生の更新SQLを実行する方法

11