Безопасность Ruby
Язык программирования Ruby объёмен и сложен, и начинающие, и опытные разработчики Ruby часто сталкиваются с проблемами безопасности.
В данном документе рассматриваются многие из этих проблем и предлагаются более безопасные альтернативы, где это возможно.
Пожалуйста, ознакомьтесь со списком общеизвестных уязвимостей CVE и правильным способом сообщения о проблемах безопасности по адресу: www.ruby-lang.org/en/security/ Японская версия находится здесь: www.ruby-lang.org/ja/security/
Проблемы безопасности следует сообщать по электронной почте на адрес security@ruby-lang.org (открытый ключ PGP), который является закрытым списком рассылки. Сообщенные проблемы будут опубликованы после исправления.
Marshal.load
Модуль Marshal языка Ruby предоставляет методы для сериализации и десериализации деревьев объектов Ruby в двоичный формат данных и обратно.
Никогда не используйте Marshal.load для десериализации недоверенных или предоставленных пользователем данных. Поскольку Marshal может десериализовать практически любой объект Ruby и имеет полный контроль над переменными экземпляра, можно создать вредоночную полезную нагрузку, которая выполнит код сразу после десериализации.
Если вам необходимо десериализовать недоверенные данные, следует использовать JSON, так как он может возвращать только «примитивные» типы, такие как строки, массивы, хэши, числа и nil. Если вам нужно десериализовать другие классы, вы должны обработать это вручную. Никогда не десериализуйте в класс, заданный пользователем.
YAML
YAML — популярный удобочитаемый формат сериализации данных, используемый многими программами Ruby для конфигурации и сохранения данных о деревьях объектов Ruby в базе данных.
Аналогично Marshal, он может десериализовать произвольные классы Ruby. Например, следующие данные YAML создадут объект ERB при десериализации:
!ruby/object:ERB src: puts `uname`
Из-за этого многие соображения безопасности, относящиеся к Marshal, также применимы к YAML. Не используйте YAML для десериализации недоверенных данных.
Символы
Символы часто рассматриваются как синтаксический сахар для простых строк, но они играют гораздо более важную роль. Реализация MRI Ruby использует символы во внутреннем представлении имен методов, переменных и констант. Причина этого в том, что символы — это просто целые числа с прикреплёнными к ним именами, поэтому их быстрее искать в хеш-таблицах.
Начиная с версии 2.2, большинство символов могут быть собраны сборщиком мусора; они называются временными символами. Большинство символов, которые вы создаёте (например, вызвав to_sym), являются временными.
Бессмертные символы, с другой стороны, никогда не будут собраны сборщиком мусора. Они создаются при модификации кода:
-
определении метода (например, с помощью
define_method), -
установлении переменной экземпляра (например, с помощью
instance_variable_set), -
создании переменной или константы (например, с помощью
const_set)
Расширения C, которые не были обновлены и всё ещё вызывают `SYM2ID`, создают бессмертные символы. Ошибки в 2.2.0: send и +__send__+ также создавали бессмертные символы, а вызов методов с ключевыми аргументами также мог их создавать.
Не создавайте бессмертные символы из пользовательского ввода. В противном случае пользователь сможет организовать атаку типа «отказ в обслуживании» против вашего приложения, заполнив его уникальными строками, что приведёт к неограниченному росту памяти до тех пор, пока процесс Ruby не будет убит или система не замедлится до полной остановки.
Хотя может не быть хорошей идеей вызывать их с пользовательским вводом, методы, которые раньше были уязвимы, такие как to_sym, respond_to?, method, instance_variable_get, const_get, и т. д. больше не представляют угрозы.
Регулярные выражения
Синтаксис регулярных выражений в Ruby имеет некоторые незначительные отличия от других языков. В Ruby якоря ^ и $ не относятся к началу и концу строки, а к началу и концу строки.
Это означает, что если вы используете регулярное выражение, например, /^[a-z]+$/ для ограничения строки только буквами, злоумышленник может обойти эту проверку, передав строку, содержащую букву, затем новую строку, а затем любую строку по своему выбору.
Если вы хотите сопоставить начало и конец всей строки в Ruby, используйте якоря \A и \z.
eval
Никогда не передавайте недоверенный или контролируемый пользователем ввод в eval.
Если вы не реализуете REPL, как irb или pry, eval почти наверняка не то, что вам нужно. Не пытайтесь отфильтровать пользовательский ввод перед передачей его в eval — этот подход чреват опасностями и, скорее всего, сделает ваше приложение уязвимым к серьёзной уязвимости удаленного выполнения кода.
send
«Глобальные функции» в Ruby (puts, exit, и т. д.) на самом деле являются закрытыми методами экземпляра в Object. Это означает, что эти методы можно вызвать с помощью send, даже если вызов send имеет явное получатель.
Например, следующий фрагмент кода выводит «Hello world» в терминал:
1.send(:puts, "Hello world")
Никогда не вызывайте send с пользовательским вводом в качестве первого параметра. Это может привести к уязвимости «отказ в обслуживании»:
foo.send(params[:bar]) # params[:bar] is "exit!"
Если злоумышленник может контролировать первые два аргумента в send, возможно удаленное выполнение кода:
# params is { :a => "eval", :b => "...ruby code to be executed..." }
foo.send(params[:a], params[:b])
При отправке вызова метода на основе пользовательского ввода тщательно проверяйте имя метода. Если возможно, проверьте его по белому списку безопасных имён методов.
Обратите внимание, что использование public_send также опасно, так как сам send является общедоступным:
1.public_send("send", "eval", "...ruby code to be executed...")
DRb
Поскольку DRb позволяет удалённым клиентам вызывать произвольные методы, его нельзя оставлять доступным для недоверенных клиентов.
При использовании DRb, старайтесь избегать его экспонирования в сети, если это возможно. Если это невозможно, и вам нужно экспонировать DRb во внешнюю сеть, вы обязательно должны настроить соответствующую политику безопасности с помощью DRb::ACL.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.