Фильтрация сериализации в Java — предотвращение уязвимостей безопасности нулевого дня

Фильтрация сериализации в Java — предотвращение уязвимостей безопасности нулевого дня

22 февраля 2023 г.

Я работаю Java-разработчиком достаточно долго, чтобы помнить волнение, когда Sun представила концепцию сериализации в JVM. В мире C мы могли просто записать структуру в файл, но это всегда было проблематично. Он не был портативным и имел много проблем. Но для Java мы могли просто написать класс, и он «сработал». Это было чистой магией!

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

https://www.youtube.com/watch?v=xLXFhRLkxLc&embedable=true

На самом деле, сразу после того, как я создал это видео, появился новый обнаружена уязвимость десериализации в SnakeYaml. Сериализация — одна из самых больших проблем безопасности во многих языках программирования, а не только проблема JVM. Хакеры могут использовать инструменты, предназначенные для доставки цепочки эксплойтов сериализации. Затем вы можете создать гаджет, используемый для доставки эксплойта, не слишком хорошо разбираясь в системе. Это страшно...

Я не эксперт по безопасности; Меня больше интересует решение. Как нам убедиться, что следующий нулевой день не повлияет на нас?

Как защитить код сервера от атак сериализации?

Нужна ли нам сериализация?

Нам редко нужна сериализация. В идеале, если вы можете полностью удалить сериализацию из своего кода и можете избежать стороннего кода, который использует сериализацию, вы можете просто полностью заблокировать ее. Это будет означать, что даже если наступит нулевой день, часть сериализации завершится ошибкой. У вас может быть ошибка, но это не будет уязвимостью, которую можно использовать.

Иногда нам нужна небольшая сериализация. В этом случае мы можем включить только необходимые известные классы и заблокировать все остальное.

JEP 290: фильтрация сериализации

Решение появилось в Java 9 в виде фильтрации сериализации как части JEP 290. Существуют критические обновления исправлений для старых JDK, таких как JDK 8u121. Поэтому, если вам необходимо использовать более старую версию, вы все равно можете использовать эту функцию.

Эту функцию можно использовать без изменения кода, хотя мы можем захотеть изменить код для дополнительных функций. Фундаментальная проблема заключается в проверке того, что важные системы не ломаются. Мы можем столкнуться с трудностями при проверке использования сериализации, например. в слоях распределенного кэширования. Кэш может сериализовать объекты для их синхронизации между узлами по сети. Мы можем пропустить эту зависимость при локальном запуске тестов, но потерпеть неудачу в рабочей среде. В таких случаях вы можете следовать стратегиям, перечисленным ниже, чтобы решить проблемы.

Белый и черный список

При фильтрации определенных сериализуемых объектов мы можем использовать два подхода:

* Белый список — блокируйте все и разрешайте определенные классы или пакеты. * Черный список — заблокируйте определенные проблемные классы и пакеты.

Черный список позволяет нам блокировать известные уязвимости, и этого может быть достаточно. Но у нас нет гарантии, что мы заблокировали все. Белый список обычно является более безопасным вариантом, но он может нарушить работу вашего кода, если вы пропустите класс, необходимый в крайнем случае.

Мы можем установить фильтр в самом JDK, отредактировав файл свойств java.security. Это может иметь смысл, если вы упаковываете JDK вместе со своим приложением. Лично я предпочитаю использовать аргумент командной строки для настройки, например:

java “-Djdk.serialFilter=!*” -jar MyJar.jar

Эта команда заблокирует всю сериализацию. Обратите внимание, что мне нужно использовать кавычки, чтобы bash не расширял знак зодиака. Восклицательный знак означает, что мы хотим заблокировать, а звездочка означает, что мы блокируем все.

Следующий код представляет собой черный список. Мы блокируем определенный пакет. Мы также можем сузить его до определенного класса. Но, как я уже говорил, это не идеально:

java “-Djdk.serialFilter=!mypackage.*” -jar MyJar.jar

Помимо проблем, присущих черному списку, основная проблема заключается в том, чтобы знать, что блокировать. Есть очевидные цели, такие как классы, которые были уязвимы в прошлом, например:

* java.rmi.server.UnicastRemoteObject * java.util.logging.Handler * java.util.zip.Inflater * org.apache.commons.collections.functors.InvokerTransformer * org.apache.commons.collections4.functors.InvokerTransformer

К сожалению, этот список не является исчерпывающим, и я не смог найти ни одного списка, который я мог бы использовать в качестве источника для правильного черного списка. Мы можем просмотреть базу данных Common Vulnerabilities and Exposures (CVE) на наличие эксплойтов, но это кропотливая работа.

Наконец, у нас есть белый список, в котором мы разрешаем классы в пакете mypackage. Мы можем сериализовать их, как обычно. JVM плавно блокирует все остальное. Это довольно близко к идеальной ситуации. При необходимости мы можем добавить дополнительные классы и пакеты, добавив их и разделив точкой с запятой:

java “-Djdk.serialFilter=mypackage.*;!*” -jar MyJar.jar

А как насчет сложности?

Как узнать, какие классы сериализованы в коде? Как получить предупреждение, если ваш код блокирует попытку сериализации? Это может быть то, что вы хотели бы отследить, поскольку это может быть сбой системы или попытка взлома. Оба являются уважительными причинами для предупреждения. Вы не можете сделать это декларативно, но вы можете написать код, который может использовать сложную логику, чтобы определить, должна ли сериализация быть успешной.

Это пример простого фильтра сериализации из документации Oracle. Обратите внимание, что он может отклонить сериализацию или оставить ее нерешенной. Это часть цепочки фильтров, где каждый этап процесса проверки может отклонить сериализацию или передать ее следующему этапу. Мы можем привязать фильтр глобально, как здесь, или сделать это отдельно для каждого потока. API удивительно гибкий и предоставляет много информации о процессе:

ObjectInputFilter.Config.setSerialFilter(info -> info.depth() > 10 ? Status.REJECTED : Status.UNDECIDED);

TL;DR

При развертывании JVM всегда следует использовать фильтрацию сериализации. Так должно быть всегда.

Фильтрация сериализации была перенесена в более ранние версии JVM, так что этому нет абсолютно никаких оправданий.

Фильтрация сериализации не требует изменения кода, и мы можем включить ее через глобальную конфигурацию или командную строку.

По крайней мере, вы можете использовать его для внесения в черный список известных уязвимостей. В идеале мы должны заблокировать всю сериализацию и добавить определенные классы или пакеты в белый список по мере необходимости.


Также опубликовано здесь


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE