Каково влияние безопасности десериализации ненадежных данных на Java?

Безопасно ли десериализовать недоверенные данные, если в моем коде не делаются предположения о состоянии или классе десериализованного объекта или может ли простой акт десериализации вызвать нежелательную операцию?

(Модель угрозы: злоумышленник может свободно изменять сериализованные данные, но все, что он может сделать)

Ответ 1

Сама десериализация уже может быть небезопасной. Сериализуемый класс может определять метод readObject (см. Также спецификация), который вызывается, когда объект этого класса будет десериализован из потока. Злоумышленник не может предоставить этот код, но используя обработанный ввод, он может вызывать любой такой метод readObject, который находится в вашем пути к классам, с любым вводом.

Ввод кода

Можно выполнить реализацию readObject, которая открывает дверь для произвольной инъекции байт-кода. Просто прочитайте байтовый массив из потока и передайте его в ClassLoader.defineClass и ClassLoader.resolveClass() (см. Javadoc для предыдущий и позже). Я не знаю, каким будет использование такой реализации, но это возможно.

Утечка памяти

Написание безопасных методов readObject сложно. До несколько недавно метод readObject HashMap содержал следующие строки.

int numBuckets = s.readInt();
table = new Entry[numBuckets];

Это делает очень легким для злоумышленника выделение нескольких гигабайт памяти всего за несколько десятков байтов сериализованных данных, которые мгновенно потеряют вашу систему с помощью OutOfMemoryError.

текущая реализация Hashtable по-прежнему остается уязвимой для подобной атаки; он вычисляет размер выделенного массива на основе количества элементов и коэффициента загрузки, но нет защиты от необоснованных значений в loadFactor, поэтому мы можем легко запросить выделение миллиарда слотов для каждого элемента таблицы,

Чрезмерная загрузка процессора

Исправление уязвимости в HashMap было сделано как часть изменений для решения другой проблемы безопасности, связанной с картами, основанными на хэше. CVE-2012-2739 описывает атаку отказа в обслуживании, основанную на потреблении процессора, создавая HashMap с очень многими конфликтующими клавишами (т.е. различные ключи с то же значение хэш-функции). Задокументированные атаки основаны на параметрах запроса в URL-адресах или ключах в HTTP POST-данных, но десериализация HashMap также уязвима для этой атаки.

меры предосторожности, которые были помещены в HashMap для предотвращения такого типа атаки, сосредоточены на картах с помощью клавиш String. Это достаточно для предотвращения атак на основе HTTP, но их легко обойти с десериализацией, например. путем упаковки каждого String с помощью ArrayList (чей хэш-код также предсказуемый). Java 8 включает предложение (JEP-180) для дальнейшего улучшения поведения HashMap перед лицом многих столкновений, что расширяет защиту до все типы ключей, которые реализуют Comparable, но все же позволяют атаковать на основе ключей ArrayList.

Таким образом, злоумышленник может создавать байтовые потоки, так что усилие ЦП, затрачиваемое на десериализацию объекта из этого потока, растет квадратично с размером потока.

Резюме

Контролируя ввод в процесс десериализации, злоумышленник может инициировать вызов любого метода десериализации readObject. Теоретически возможно, чтобы такой метод позволял вводить байт-код. На практике, конечно, можно легко вывести ресурсы памяти или процессора таким образом, что приводит к атакам типа "отказ в обслуживании". Аудит вашей системы на такие уязвимости очень сложный: вам нужно проверить каждую реализацию readObject, в том числе в сторонних библиотеках и библиотеке времени выполнения.