FlawOpen

flawopen.com/Ruby YAML/Ruby YAML

Безопасен ли YAML.load для недоверенных данных?

CWE-502: Deserialization of Untrusted DataСправочная страница
Краткий ответ

Проверьте фактически загруженную версию Psych: Psych 4, входящий в Ruby 3.1, изменил YAML.load, включив безопасную десериализацию по умолчанию. Более ранние версии Psych создавали произвольные объекты; Psych также можно обновить отдельно от Ruby. YAML.unsafe_load и Marshal.load десериализуют объекты Ruby, поэтому им нельзя передавать недоверенные данные.

VULNERABLE
# Psych < 4: YAML.load used unsafe
# object construction by default.
YAML.load(params[:config])

# Every Psych version: explicitly unsafe.
YAML.unsafe_load(user_input)
Psych.unsafe_load(user_input)

# Marshal has no restricted safe-load API.
Marshal.load(cookie_value)
FIXED
# Explicit restricted types; keep this list minimal.
require "yaml"
require "date"

data = YAML.safe_load(
  input,
  permitted_classes: [Date],
  permitted_symbols: [],
  aliases: false
)

# For ordinary external data, prefer JSON and
# validate its shape, values, and input size.
data = JSON.parse(input)

Почему YAML может быть опасен

YAML содержит теги, которые Psych может преобразовывать в объекты Ruby. При небезопасной загрузке документ с тегом может запросить создание класса и задать значения его сериализованного состояния; последствия зависят от доступных классов и их поведения. Так строятся цепочки гаджетов десериализации. Загружайте недоверенный YAML безопасно и отдельно проверяйте полученные данные. Аналогичный общий риск есть у Python pickle, PHP unserialize и Ruby Marshal.load.

Rails и другие приложения могут загружать множество зависимостей, расширяя набор классов и поведения, доступных небезопасной цепочке десериализации. Это зависит от конкретного развертывания и не является свойством каждого Rails-приложения или YAML-документа.

Почему смена версии вызвала путаницу

В Psych 4 метод YAML.load по умолчанию стал использовать безопасную загрузку, а unsafe_load сохранил прежнее поведение с созданием объектов. Psych 4 входил в Ruby 3.1, но приложение может выбрать другую версию gem Psych. Проверяйте разрешённую зависимость и runtime, а не только Ruby. Безопасная загрузка может отклонять классы приложения или aliases; мигрируйте с минимальным явным списком разрешённых классов, а не переходите на unsafe_load.

Если после обновления появился unsafe_load, считайте вызов проблемой безопасности, если ввод может контролироваться за пределами доверенной границы развёртывания. Используйте safe_load с минимальным списком permitted_classes; не расширяйте его только ради подавления ошибок совместимости.

FAQ

Частые вопросы

Полностью ли безопасен YAML.safe_load?

Psych.safe_load ограничивает классы десериализации разрешённым набором (по умолчанию — простые скалярные типы и коллекции) и отклоняет aliases. Но метод не проверяет структуру, размер или бизнес-правила приложения. Разрешайте только необходимые классы, ограничивайте размер и проверяйте результат. В некоторых YAML-реализациях и процессах были DoS из-за раскрытия aliases; CVE-2024-35221 описывает случай при разборе метаданных RubyGems. Это не означает, что уязвим каждый вызов Psych.safe_load.

Что если я загружаю собственные файлы конфигурации?

Файл внутри доверенного и защищённого от изменений артефакта приложения имеет другую историю происхождения, чем пользовательская загрузка или файл, контролируемый арендатором. Даже для внутренней конфигурации предпочтителен safe_load с разрешением только нужных классов. Применяйте unsafe_load только к неизменяемым и доказанно доверенным данным, никогда — к пользовательскому или зависящему от запроса содержимому.

Допустим ли Marshal?

Отдельный риск Ruby: Marshal.load

Отдельно от YAML: документация Ruby указывает, что Marshal.load может восстановить почти любой класс, загруженный в процесс, и привести к удалённому выполнению кода при недоверенных данных. Храните данные Marshal только внутри доверенной границы; никогда не десериализуйте пользовательские байты или данные, на которые мог повлиять атакующий.

Источники

См. также Небезопасная десериализация: риски и защита | flawopen.com .NET BinaryFormatter: риски и переход Безопасность ObjectInputStream в Java: защита десериализации Newtonsoft.Json TypeNameHandling: безопасная настройка? PHP unserialize() и недоверенные данные Python pickle.load(): риск недоверенных данных