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 с динамической привязкой в рельсах