Spec-Zone.ru › Ruby 3.4

Безопасность 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 с помощью метода ‘unsafe_load` создадут объект ERB:

!ruby/object:ERB
src: puts `uname`

Поэтому многие соображения безопасности, относящиеся к Marshal, применимы и к YAML. Не используйте YAML для десериализации недоверенных данных.

Символы

Символы часто воспринимаются как синтаксический сахар для простых строк, но они играют гораздо более важную роль. Реализация Ruby MRI использует символы внутри для имён методов, переменных и констант. Причина в том, что символы — это целые числа с присвоенными им именами, поэтому поиск по ним в хеш-таблицах выполняется быстрее.

Начиная с версии 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–2025 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API