openjdk.ruOpenJDK на русском

JEP 290: Filter Incoming Serialization Data

Фильтрация входящих данных сериализации

ОтветственныйRoger Riggs
ТипFeature
ОбластьSE
СтатусClosed / Delivered
Выпуск9
Компонентcore-libs / java.io:serialization
Обсуждениеcore dash libs dash dev at openjdk dot java dot net
ТрудоёмкостьS
ДлительностьS
Связан сJEP 415: Context-Specific Deserialization Filters
РецензентыAlan Bateman, Andrew Gross, Brian Goetz
ОдобренBrian Goetz
Создан2016/04/22 16:06
Обновлён2022/08/15 16:17
Задача8154961

Аннотация

Разрешить фильтрацию входящих потоков данных сериализации объектов, чтобы повысить и безопасность, и надёжность.

Цели

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

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

  • Предоставить механизм, с помощью которого объекты, экспортированные через RMI, смогут проверять классы, ожидаемые в вызовах.

  • Механизм фильтрации не должен требовать создания подклассов ObjectInputStream или изменения существующих подклассов.

  • Определить глобальный фильтр, который можно настроить через свойства или конфигурационный файл.

Что не является целью

  • Определять или поддерживать какую-либо конкретную политику того, какие классы следует разрешать или запрещать.

  • Исправлять проблемы сложности или целостности в конкретных классах или реализациях.

  • Предоставлять возможность заглядывать вперёд в потоке.

  • Предоставлять детальный доступ к содержимому объектов.

Критерии успеха

  • Минимальное измеримое влияние на производительность при простом запрете классов по списку

Мотивация

Рекомендации по безопасности неизменно требуют, чтобы входные данные из внешних источников проверялись перед использованием. Механизм фильтрации позволит клиентам сериализации объектов проще проверять свои входные данные, а экспортированным объектам RMI — проверять аргументы вызовов.

Описание

Основной механизм — интерфейс фильтра, который реализуют клиенты сериализации и который устанавливается для ObjectInputStream. Методы интерфейса фильтра вызываются в процессе десериализации, чтобы проверять десериализуемые классы, размеры создаваемых массивов, а также метрики, описывающие длину потока, глубину потока и количество ссылок, по мере декодирования потока. Фильтр возвращает статус: принять, отклонить или оставить статус неопределённым.

Для каждого нового объекта в потоке фильтр вызывается с классом этого объекта до того, как объект будет создан и десериализован. Для примитивов и экземпляров java.lang.String, которые закодированы в потоке непосредственно, фильтр не вызывается. Для каждого массива, независимо от того, является ли он массивом примитивов, массивом строк или массивом объектов, фильтр вызывается с классом массива и длиной массива. Для каждой ссылки на объект, уже прочитанный из потока, фильтр вызывается, чтобы он мог проверить глубину, количество ссылок и длину потока. Действия фильтра записываются в журнал java.io.serialization, если журналирование включено.

В случае RMI объект экспортируется через UnicastServerRef, который устанавливает фильтр для MarshalInputStream, чтобы проверять аргументы вызова по мере их демаршалинга. Экспорт объектов через UnicastRemoteObject должен поддерживать установку фильтра, используемого при демаршалинге.

Фильтр для всего процесса

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

  • Системное свойство jdk.serialFilter
  • Свойство безопасности jdk.serialFilter в conf/security/java.security

Фильтр задаётся как последовательность шаблонов; каждый шаблон либо сопоставляется с именем класса в потоке, либо является ограничением. Шаблоны разделяются символом «;» (точка с запятой). Пробельные символы значимы и считаются частью шаблона.

Шаблон ограничения содержит «=» и задаёт ограничение. Если ограничение встречается больше одного раза, используется последнее значение. Если какое-либо из значений в вызове ObjectInputFilter.checkInput(...) превышает соответствующее ограничение, фильтр возвращает Status.REJECTED. Ограничения проверяются раньше классов независимо от их порядка в последовательности шаблонов.

  • maxdepth=value — максимальная глубина графа
  • maxrefs=value — максимальное количество внутренних ссылок
  • maxbytes=value — максимальное количество байтов во входном потоке
  • maxarray=value — максимально допустимый размер массива

Остальные шаблоны, слева направо, сопоставляются с именем класса или пакета, возвращаемым Class::getName. Если класс является типом массива, сопоставляется класс или пакет типа элемента. Массивы любой размерности обрабатываются так же, как тип элемента. Например, шаблон «!example.Foo» отклоняет создание любого экземпляра или массива example.Foo.

  • Если шаблон начинается с «!», класс отклоняется, если остальная часть шаблона совпадает, иначе он принимается
  • Если шаблон содержит «/», непустой префикс до «/» является именем модуля. Если имя модуля совпадает с именем модуля класса, оставшаяся часть шаблона сопоставляется с именем класса. Если «/» нет, имя модуля не сравнивается.
  • Если шаблон заканчивается на «.**», он совпадает с любым классом в пакете и во всех подпакетах
  • Если шаблон заканчивается на «.*», он совпадает с любым классом в пакете
  • Если шаблон заканчивается на «*», он совпадает с любым классом, имя которого начинается с этого шаблона.
  • Если шаблон равен имени класса, он совпадает.
  • В противном случае статус не определён.

Интерфейс ObjectInputFilter и API

Интерфейс фильтра входных объектов реализуется клиентами RMI и сериализации и обеспечивает поведение настраиваемого фильтра для всего процесса.

interface ObjectInputFilter {
    Status checkInput(FilterInput filterInfo);

    enum Status { 
        UNDECIDED, 
        ALLOWED, 
        REJECTED; 
    }

   interface FilterInfo {
         Class<?> serialClass();
         long arrayLength();
         long depth();
         long references();
         long streamBytes();
   }

    public static class Config {
        public static void setSerialFilter(ObjectInputFilter filter);
        public static ObjectInputFilter getSerialFilter(ObjectInputFilter filter) ;
        public static ObjectInputFilter createFilter(String patterns);
    }   
}

Фильтр ObjectInputStream

У ObjectInputStream есть дополнительные методы для установки и получения текущего фильтра. Если для ObjectInputStream фильтр не установлен, используется глобальный фильтр, если он задан.

public class ObjectInputStream ... {
    public final void setObjectInputFilter(ObjectInputFilter filter);
    public final ObjectInputFilter getObjectInputFilter(ObjectInputFilter filter);
}

Альтернативы

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

Тестирование

Обновлять существующие тесты не требуется. Новые модульные тесты будут проверять механизмы фильтрации на сериализованных потоках, объектах, экспортированных через RMI, и глобальном механизме фильтрации.

Риски и допущения

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

Новые API и интерфейсы будут добавлены в JDK 9. Перенос этой возможности в предыдущие версии потребует добавления API, специфичных для реализации, чтобы избежать изменений в более старых версиях спецификации Java SE.