Rails 5 SQL Injection

Я читал об этом в течение некоторого времени в разных 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

Я пытаюсь написать адаптер "raw query", который позволяет запускать необработанные запросы Postgres, но позволяет мне вставлять мои собственные параметры, которые поступают из опасного пользовательского ввода.

Я не могу использовать AR в этих нескольких случаях, потому что я выполняю сложные вычисления lat/long, агрегированные функции, сложные подзапросы и т.д.

До сих пор я пробовал 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])

Кроме того, кажется, что sanitize делегирует quote. Но в соответствии с этим SO post в нем говорится, что просто обертывание вещей одинарными кавычками небезопасно... поэтому я понятия не имею.

Метод 2

Я не уверен, что это так же безопасно, но, похоже, использует фактическую подготовленную PG-функцию (которая, как я полагаю, на 100% безопасна). Единственная проблема заключается в том, что рельсы не распечатывают ее на консоль, а также не включают время выполнения 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-мудрый, и мне нужна хорошая библиотека, которую я могу включить во все мои проекты, которые, как я знаю, абсолютно безопасны.

Ответы

Ответ 1

Использование 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 году и некоторыми альтернативами: Как выполнить необработанное обновление sql с динамической привязкой в ​​рельсах