Безопасность Ruby
Язык программирования Ruby большой и сложный, и начинающие, и опытные разработчики Ruby часто сталкиваются с многочисленными проблемами безопасности.
Этот документ призван обсудить многие из этих проблем и предложить более безопасные альтернативы, где это возможно.
Пожалуйста, ознакомьтесь со списком всех общеизвестных уязвимостей (CVE) и правильным способом сообщения о проблемах безопасности по адресу: www.ruby-lang.org/en/security/ Версия на японском языке находится здесь: www.ruby-lang.org/ja/security/
Проблемы безопасности следует сообщать по электронной почте на адрес security@ruby-lang.org (открытый ключ PGP), что является закрытым почтовым списком. Сообщенные проблемы будут опубликованы после исправления.
$SAFE
Ruby предоставляет механизм для ограничения операций, которые может выполнять код Ruby, в виде переменной $SAFE.
Однако, $SAFE не предоставляет безопасную среду для выполнения небезопасного кода.
Если вам нужно выполнить небезопасный код, вы должны использовать механизм изоляции на уровне операционной системы. В Linux для изоляции потенциально вредоносного кода можно использовать ptrace или LXC. Аналогичные механизмы существуют на каждой крупной операционной системе.
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.