В современном мире безопасности программного обеспечения вопросы надёжной обработки данных являются критически важными для обеспечения безопасности веб-приложений и сервисов. Одной из распространённых проблем, способных привести к серьёзным уязвимостям, являются атаки, связанные с десериализацией объектов. В контексте языка программирования Ruby и широко используемой библиотеки для обработки JSON — Oj (Optimized JSON), — этот вопрос приобретает особую значимость. Рассмотрим, почему на первый взгляд безобидный парсер JSON может стать источником серьёзных угроз и как можно защититься от подобных рисков. В основе проблемы лежит механизм, с помощью которого парсер Oj обрабатывает JSON-данные и преобразует их в объекты языка Ruby.
Oj позиционируется как максимально быстрый и эффективный парсер JSON, предлагающий различные режимы работы, включая режим объекта, в котором JSON-документы могут быть десериализованы непосредственно в Ruby-объекты. Одним из ключевых моментов, вызывающих тревогу, является типичная конфигурация Oj по умолчанию: в ней строковые значения JSON, начинающиеся с двоеточия, автоматически преобразуются в Ruby-символы. В языке Ruby символы широко используются как ключи в хэшах, имена методов, а также — что важно — как индикаторы имён столбцов в базах данных при работе с ORM-библиотеками, например Sequel. Когда JSON, поступающий в веб-приложение, содержит строку с двоеточием, он автоматически превращается в Ruby-символ, что может привести к неожиданным и опасным последствиям. Такое поведение может использоваться злоумышленниками для проведения атак на уровне приложения и базы данных.
Например, при обновлении информации о пользователе через JSON-запрос, если поле имя преобразуется в символ и передаётся ORM-библиотеке, это может привести к формированию SQL-запроса с использованием имени столбца вместо строки. Это означает, что злоумышленник, отправив специальный JSON с именем типа «:admin_notes», может подменить значение на содержимое защищённого столбца, что фактически открывает доступ к конфиденциальным данным, таких как внутренние заметки администратора или даже хеши паролей. Ещё более серьёзной угрозой является то, что возможность десериализации Ruby-объектов из JSON предоставляет потенциальный вектор для выполнения произвольного кода на сервере. Атаки с удалённым выполнением кода (Remote Code Execution, RCE), использующие десериализацию, являются одними из наиболее опасных и сложных для обнаружения. Таким образом, даже если ошибка не проявляется напрямую, факт использования парсера Oj без надлежащей настройки и понимания возможных рисков ставит безопасность приложения под угрозу.
Характерной проблемой является то, что разработчики, использующие Oj в своих проектах, часто делают это по умолчанию, следуя официальной документации или советам из интернета, не подозревая о подводных камнях. Документация Oj советует использовать функцию Oj.load для парсинга JSON без особых предупреждений о рисках десериализации объектов, кроме краткого упоминания в разделе безопасности, которое часто остается незамеченным. Наличие этой уязвимости — этапная иллюстрация более общей проблемы с безопасностью программного обеспечения: многие инструменты и библиотеки являются «небезопасными по умолчанию». При этом пользователи полагаются на стандартную конфигурацию, не осознавая, что используют потенциально опасные функции без дополнительной защиты.
Иррационально ожидать, что все разработчики будут тщательно изучать детали реализации и предосторожности, особенно когда основная цель — быстрое и эффективное выполнение бизнес-задач. На практике, идеальным решением было бы, чтобы разработчики библиотек обеспечивали безопасные режимы работы по умолчанию, а рискованные функции маркировались явно, подталкивая пользователя к более осознанному выбору. Именно этого не хватает в случае парсера Oj, где основная функция парсинга JSON может создавать объекты Ruby и символы автоматически, что несёт риск. Чтобы минимизировать угрозу, существует несколько рекомендаций, которые можно и нужно внедрять в проекты на Ruby, использующие Oj или его транзитивных зависимостей. Во-первых, следует проверить свои проекты на наличие oj в списках зависимостей — даже если вы не используете парсер напрямую, он может попадать в проект через библиотеки, что создаёт потенциальные скрытые уязвимости.
Продвинутым и верным решением является отказ от использования Oj для парсинга JSON, где это возможно, и переход на стандартный JSON-парсер Ruby, который традиционно работает безопаснее, превращая JSON-строки только в строки, а не в объекты с исполнением кода. В случаях, когда производительность критична и переход на json гему невозможен, необходимо использовать режим strict в Oj. Этот режим ограничивает десериализацию объектов и символов, превращая строки в строки и предотвращая автоматическое создание Ruby-символов из строк, начинающихся с двоеточия. Для этого достаточно добавить в инициализационный код приложения строку настройки Oj.default_options = { mode: :strict }, которая заставит парсер действовать более осторожно.
Кроме того, если работа с объектной десериализацией абсолютно необходима, например, для восстановления объектов бизнес-логики, следует вместо Oj.load использовать Oj.safe_load, который имеет возможность ограничивать классы, которые будут создаваться при загрузке, предотвращая создание произвольных объектов и связанных с этим RCE уязвимостей. Такой подход особенно важен для зависимостей и библиотек, где контроль над форматом входных данных может быть не таким жёстким. Рекомендуется также устанавливать комплексную систему мониторинга и логирования ошибок, чтобы быстро обнаруживать аномалии в работе приложений, которые могут быть связаны с попытками атак через особенности десериализации.
Подводя итог, атаки на основе десериализации объектов в Ruby через парсер JSON Oj представляют собой реальную и серьёзную угрозу для безопасности веб-приложений. Особенно серьёзно осложняет дело то, что эти уязвимости часто остаются незамеченными из-за того, что поведение парсера по умолчанию может не вызывать подозрений у разработчиков и пользователей. Осознание проблемы, внимательный анализ зависимостей и грамотное использование настроек безопасности парсера позволяют существенно снизить риск атак и защитить критичные данные и функционал приложения. Таким образом, каждому разработчику, администратору и специалисту по безопасности следует учитывать указанные аспекты и внедрять лучшие практики при работе с JSON в Ruby, чтобы сохранить безопасность и надёжность своих систем.