Безопасность Ruby
Язык программирования Ruby большой и сложный, и начинающие, и опытные разработчики Ruby часто сталкиваются с многочисленными проблемами безопасности.
В этом документе рассматриваются многие из этих проблем и предлагаются более безопасные альтернативы, где это возможно.
Пожалуйста, ознакомьтесь со полным списком общеизвестных уязвимостей CVEs и правильным способом сообщения об уязвимостях безопасности по адресу: 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 при десериализации, используя метод «unsafe_load»:
!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–2024 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.